tkgoodstuff/libs/tkgUnixButton.c

569 lines
17 KiB
C
Raw Permalink Normal View History

/*
* tkUnixButton.c --
*
* This file implements the Unix specific portion of the button
* widgets.
*
* Copyright (c) 1996 by Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* SCCS: @(#) tkUnixButton.c 1.3 96/11/07 20:00:02
*/
#include "tkgButton.h"
#include "default.h"
#include "tkPort.h"
#define max(a,b) (a > b ? a : b)
#define min(a,b) (a < b ? a : b)
/*
* Declaration of Unix specific button structure.
*/
typedef struct UnixTkgButton {
TkgButton info; /* Generic button info. */
} UnixTkgButton;
/*
* The class procedure table for the button widgets.
*/
TkClassProcs tkpTkgButtonProcs = {
NULL, /* createProc. */
TkgButtonWorldChanged, /* geometryProc. */
NULL /* modalProc. */
};
/*
*----------------------------------------------------------------------
*
* TkpCreateButton --
*
* Allocate a new TkButton structure.
*
* Results:
* Returns a newly allocated TkButton structure.
*
* Side effects:
* Registers an event handler for the widget.
*
*----------------------------------------------------------------------
*/
TkgButton *
TkpCreateTkgButton(tkwin)
Tk_Window tkwin;
{
UnixTkgButton *butPtr = (UnixTkgButton *)ckalloc(sizeof(UnixTkgButton));
return (TkgButton *) butPtr;
}
/*
*----------------------------------------------------------------------
*
* TkpDisplayButton --
*
* This procedure is invoked to display a button widget. It is
* normally invoked as an idle handler.
*
* Results:
* None.
*
* Side effects:
* Commands are output to X to display the button in its
* current mode. The REDRAW_PENDING flag is cleared.
*
*----------------------------------------------------------------------
*/
void
TkpDisplayTkgButton(clientData)
ClientData clientData; /* Information about widget. */
{
register TkgButton *butPtr = (TkgButton *) clientData;
register Tk_Window tkwin = butPtr->tkwin;
GC gc;
Tk_3DBorder border;
Pixmap pixmap;
int width, height; /* Width and height of button window */
int x, y; /* Where to draw image or text */
int relief; /* The button relief */
int offset; /* The movement of button contents with relif */
int lskirt, rskirt, yskirt; /* Size of unusable edge of button window */
int pixwidth, pixheight; /* size of image */
int pixxorig, pixyorig; /* up-left corner of region in button for image */
int pixwwidth, pixwheight; /* size of region in button for image */
int tilewidth, tileheight; /* size of tile image */
int textwidth, textheight; /* size of text */
int textxorig, textyorig; /* up-left corner of region in button for text */
int textwwidth, textwheight; /* size of region in button for text */
int wwidth, wheight; /* size of usable region in button */
int drawimage, drawbitmap, drawtext, drawtile; /* do we draw an image, a bitmap,
/* text, tile? */
char *side = butPtr->iconside; /* where does the image or bitmap go? */
butPtr->flags &= ~REDRAW_PENDING;
if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
return;
}
border = butPtr->normalBorder;
if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
gc = butPtr->disabledGC;
} else if ((butPtr->state == tkActiveUid)
&& !Tk_StrictMotif(butPtr->tkwin)) {
gc = butPtr->activeTextGC;
border = butPtr->activeBorder;
} else {
gc = butPtr->normalTextGC;
}
if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
&& (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
border = butPtr->selectBorder;
}
/*
* Override the relief specified for the button if this is a
* checkbutton or radiobutton and there's no indicator.
*/
relief = butPtr->relief;
if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
: TK_RELIEF_RAISED;
}
offset = ((butPtr->type == TYPE_BUTTON) ||
(butPtr->type == TYPE_MENU_BUTTON)) && !Tk_StrictMotif(butPtr->tkwin);
/*
* In order to avoid screen flashes, this procedure redraws
* the button in a pixmap, then copies the pixmap to the
* screen in a single operation. This means that there's no
* point in time where the on-sreen image has been cleared.
*/
pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
/*
* Calculate regions for image and text
*/
drawimage = drawbitmap = drawtext = drawtile = 0;
pixwidth = pixheight = textwidth = textheight = 0;
if (butPtr->image != None) {
drawimage = 1;
Tk_SizeOfImage(butPtr->image, &pixwidth, &pixheight);
} else if (butPtr->bitmap != None) {
drawbitmap = 1;
Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &pixwidth, &pixheight);
}
if (butPtr->tileImage != None) {
drawtile = 1;
Tk_SizeOfImage(butPtr->tileImage, &tilewidth, &tileheight);
}
if (*butPtr->text) {
drawtext = 1;
textwidth = butPtr->textWidth;
textheight = butPtr->textHeight;
}
width = Tk_Width(tkwin);
height = Tk_Height(tkwin);
rskirt = butPtr->inset + offset + butPtr->padX;
lskirt = rskirt + butPtr->indicatorSpace;
yskirt = butPtr->inset + offset + butPtr->padY;
wwidth = width - lskirt - rskirt;
wheight = height - 2 * yskirt;
pixxorig = textxorig = lskirt;
pixyorig = textyorig = yskirt;
pixwwidth = textwwidth = wwidth - butPtr->indicatorSpace;
pixwheight = textwheight = wheight;
if (drawtile) {
int tx, ty, tw, th;
for (tx = butPtr->inset; tx < (width - butPtr->inset); tx += tilewidth) {
for (ty = butPtr->inset; ty < (height - butPtr->inset); ty += tileheight) {
tw = min(tilewidth,width - butPtr->inset - tx);
th = min(tileheight,height - butPtr->inset - ty);
Tk_RedrawImage(butPtr->tileImage, 0, 0, tw, th, pixmap,
tx, ty);
}
}
}
if (drawtext && (drawimage || drawbitmap)) {
int iw, tw;
iw = butPtr->imageWeight;
tw = butPtr->textWeight;
if (!(iw+tw)) {
iw = 512;
} else {
iw = (int) (iw*1024)/(iw+tw);
}
/* we're drawing both; divide extra space according to weights*/
if (!strcmp(side,LEFT) || !strcmp(side,RIGHT)) {
pixwwidth = pixwidth +
((int) (wwidth - pixwidth - textwidth - butPtr->sep)*iw/1024);
pixwwidth = max(pixwidth,pixwwidth);
textwwidth = wwidth - pixwwidth - butPtr->sep;
textwwidth = max(textwwidth,textwidth);
if (!strcmp(side,LEFT)) {
textxorig = lskirt + pixwwidth +butPtr->sep;
} else {
pixxorig = lskirt + textwwidth +butPtr->sep;
}
} else if (!strcmp(side,BOTTOM) || !strcmp(side,TOP)) {
/* top or bottom */
pixwheight = pixheight +
((int) (wheight - pixheight - textheight - butPtr->sep)*iw/1024);
pixwheight = max(pixheight,pixwheight);
textwheight = wheight - pixwheight - butPtr->sep;
textwheight = max(textheight,textwheight);
if (!strcmp(side,BOTTOM)) {
pixyorig = yskirt + textwheight + butPtr->sep;
} else {
textyorig = yskirt + pixwheight + butPtr->sep;
}
}
}
/*
* Locate image within its region on the button, and draw it.
*/
if (drawimage || drawbitmap) {
switch (butPtr->imageAnchor) {
case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
x = pixxorig;
break;
case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
x = pixxorig + ((int) (pixwwidth - pixwidth))/2;
break;
default:
x = pixxorig + pixwwidth - pixwidth;
break;
}
switch (butPtr->imageAnchor) {
case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
y = pixyorig;
break;
case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
y = pixyorig + (pixwheight - pixheight)/2;
break;
default:
y = pixyorig + pixwheight - pixheight;
break;
}
if (relief == TK_RELIEF_RAISED) {
x -= offset;
y -= offset;
} else if (relief == TK_RELIEF_SUNKEN) {
x += offset;
y += offset;
}
if (drawimage) {
Tk_RedrawImage(butPtr->image, 0, 0, pixwidth, pixheight, pixmap,
x, y);
} else {
XSetClipOrigin(butPtr->display, gc, x, y);
XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
(unsigned int) pixwidth, (unsigned int) pixheight, x, y, 1);
XSetClipOrigin(butPtr->display, gc, 0, 0);
}
}
/*
* Locate text within its region on the button, and draw it.
*/
if (drawtext) {
switch (butPtr->textAnchor) {
case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
x = textxorig;
break;
case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
x = textxorig + ((int) (textwwidth - textwidth))/2;
break;
default:
x = textxorig + textwwidth - textwidth;
break;
}
switch (butPtr->textAnchor) {
case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
y = textyorig;
break;
case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
y = textyorig + ((int) (textwheight - textheight))/2;
break;
default:
y = textyorig + textwheight - textheight;
break;
}
if (relief == TK_RELIEF_RAISED) {
x -= offset;
y -= offset;
} else if (relief == TK_RELIEF_SUNKEN) {
x += offset;
y += offset;
}
Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
x, y, 0, -1);
Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
butPtr->textLayout, x, y, butPtr->underline);
y += textheight/2;
}
/*
* Draw the indicator for check buttons and radio buttons. At this
* point x and y refer to the top-left corner of the text or image
* or bitmap.
*/
if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
int dim;
dim = butPtr->indicatorDiameter;
x -= butPtr->indicatorSpace;
y -= dim/2;
if (dim > 2*butPtr->borderWidth) {
Tk_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim,
butPtr->borderWidth,
(butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
TK_RELIEF_RAISED);
x += butPtr->borderWidth;
y += butPtr->borderWidth;
dim -= 2*butPtr->borderWidth;
if (butPtr->flags & SELECTED) {
GC gc;
gc = Tk_3DBorderGC(tkwin,(butPtr->selectBorder != NULL)
? butPtr->selectBorder : butPtr->normalBorder,
TK_3D_FLAT_GC);
XFillRectangle(butPtr->display, pixmap, gc, x, y,
(unsigned int) dim, (unsigned int) dim);
} else {
Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y,
dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT);
}
}
} else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
XPoint points[4];
int radius;
radius = butPtr->indicatorDiameter/2;
points[0].x = x - butPtr->indicatorSpace;
points[0].y = y;
points[1].x = points[0].x + radius;
points[1].y = points[0].y + radius;
points[2].x = points[1].x + radius;
points[2].y = points[0].y;
points[3].x = points[1].x;
points[3].y = points[0].y - radius;
if (butPtr->flags & SELECTED) {
GC gc;
gc = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
? butPtr->selectBorder : butPtr->normalBorder,
TK_3D_FLAT_GC);
XFillPolygon(butPtr->display, pixmap, gc, points, 4, Convex,
CoordModeOrigin);
} else {
Tk_Fill3DPolygon(tkwin, pixmap, butPtr->normalBorder, points,
4, butPtr->borderWidth, TK_RELIEF_FLAT);
}
Tk_Draw3DPolygon(tkwin, pixmap, border, points, 4, butPtr->borderWidth,
(butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
TK_RELIEF_RAISED);
}
/*
* If the button is disabled with a stipple rather than a special
* foreground color, generate the stippled effect. If the widget
* is selected and we use a different background color when selected,
* must temporarily modify the GC.
*/
if ((butPtr->state == tkDisabledUid)
&& ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
&& (butPtr->selectBorder != NULL)) {
XSetForeground(butPtr->display, butPtr->disabledGC,
Tk_3DBorderColor(butPtr->selectBorder)->pixel);
}
XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
butPtr->inset, butPtr->inset,
(unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
(unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
&& (butPtr->selectBorder != NULL)) {
XSetForeground(butPtr->display, butPtr->disabledGC,
Tk_3DBorderColor(butPtr->normalBorder)->pixel);
}
}
/*
* Draw the border and traversal highlight last. This way, if the
* button's contents overflow they'll be covered up by the border.
*/
if (relief != TK_RELIEF_FLAT) {
int inset = butPtr->highlightWidth;
if (butPtr->isDefault) {
inset += 2;
Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
1, TK_RELIEF_SUNKEN);
inset += 3;
}
Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
butPtr->borderWidth, relief);
}
if (butPtr->highlightWidth != 0) {
GC gc;
if (butPtr->flags & GOT_FOCUS) {
gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
} else {
gc = Tk_GCForColor(butPtr->highlightBgColorPtr, pixmap);
}
Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
}
/*
* Copy the information from the off-screen pixmap onto the screen,
* then delete the pixmap.
*/
XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
(unsigned) Tk_Height(tkwin), 0, 0);
Tk_FreePixmap(butPtr->display, pixmap);
}
/*
*----------------------------------------------------------------------
*
* TkpComputeButtonGeometry --
*
* After changes in a button's text or bitmap, this procedure
* recomputes the button's geometry and passes this information
* along to the geometry manager for the window.
*
* Results:
* None.
*
* Side effects:
* The button's window may change size.
*
*----------------------------------------------------------------------
*/
void
TkpComputeTkgButtonGeometry(butPtr)
register TkgButton *butPtr; /* Button whose geometry may have changed. */
{
int pixwidth, pixheight, textwidth, textheight, width, height;
int avgWidth;
Tk_FontMetrics fm;
int drawimage, drawtext; /* Are we drawing an image? text? */
char *side;
pixwidth = pixheight = textwidth = textheight =
width = height = drawimage = drawtext = 0;
if (butPtr->highlightWidth < 0) {
butPtr->highlightWidth = 0;
}
butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
/*
* Leave room for the default ring if needed.
*/
if (butPtr->isDefault) {
butPtr->inset += 5;
}
butPtr->indicatorSpace = 0;
/*
* Get size of image or bitmap.
*/
if (butPtr->image != NULL) {
Tk_SizeOfImage(butPtr->image, &pixwidth, &pixheight);
drawimage = 1;
} else if (butPtr->bitmap != None) {
Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &pixwidth, &pixheight);
drawimage = 1;
}
if (strlen(butPtr->text) > 0) {
drawtext = 1;
Tk_FreeTextLayout(butPtr->textLayout);
butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
butPtr->text, -1, butPtr->wrapLength, butPtr->justify, 0,
&butPtr->textWidth, &butPtr->textHeight);
textwidth = butPtr->textWidth;
textheight = butPtr->textHeight;
}
side = butPtr->iconside;
if (!strcmp(side,LEFT) || !strcmp(side,RIGHT)) {
height = max(pixheight,textheight);
width = pixwidth + textwidth;
if (drawimage && drawtext) {
width += butPtr->sep;
}
} else if (!strcmp(side,BOTTOM) || !strcmp(side,TOP)) {
width = max(pixwidth,textwidth);
height = pixheight + textheight;
if (drawimage && drawtext) {
height += butPtr->sep;
}
} else {
width = max(pixwidth,textwidth);
height = max(pixheight,textheight);
}
if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
butPtr->indicatorSpace = height;
if (butPtr->type == TYPE_CHECK_BUTTON) {
butPtr->indicatorDiameter = (65*height)/100;
} else {
butPtr->indicatorDiameter = (75*height)/100;
}
}
width += butPtr->indicatorSpace;
width += 2*butPtr->padX;
height += 2*butPtr->padY;
if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
width += 2;
height += 2;
}
if (butPtr->width > 0) {
width = butPtr->width;
}
if (butPtr->height > 0) {
height = butPtr->height;
}
Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
+ 2*butPtr->inset), (int) (height + 2*butPtr->inset));
Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
}