diff options
Diffstat (limited to 'unix/tkUnixButton.c')
-rw-r--r-- | unix/tkUnixButton.c | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/unix/tkUnixButton.c b/unix/tkUnixButton.c new file mode 100644 index 0000000..8c74dcb --- /dev/null +++ b/unix/tkUnixButton.c @@ -0,0 +1,478 @@ +/* + * 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.4 97/06/06 11:21:40 + */ + +#include "tkButton.h" + +/* + * Declaration of Unix specific button structure. + */ + +typedef struct UnixButton { + TkButton info; /* Generic button info. */ +} UnixButton; + +/* + * The class procedure table for the button widgets. + */ + +TkClassProcs tkpButtonProcs = { + NULL, /* createProc. */ + TkButtonWorldChanged, /* 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. + * + *---------------------------------------------------------------------- + */ + +TkButton * +TkpCreateButton(tkwin) + Tk_Window tkwin; +{ + UnixButton *butPtr = (UnixButton *)ckalloc(sizeof(UnixButton)); + return (TkButton *) 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 +TkpDisplayButton(clientData) + ClientData clientData; /* Information about widget. */ +{ + register TkButton *butPtr = (TkButton *) clientData; + GC gc; + Tk_3DBorder border; + Pixmap pixmap; + int x = 0; /* Initialization only needed to stop + * compiler warning. */ + int y, relief; + register Tk_Window tkwin = butPtr->tkwin; + int width, height; + int offset; /* 0 means this is a label widget. 1 means + * it is a flavor of button, so we offset + * the text to make the button appear to + * move up and down as the relief changes. */ + + 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) && !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); + + /* + * Display image or bitmap or text for button. + */ + + if (butPtr->image != None) { + Tk_SizeOfImage(butPtr->image, &width, &height); + + imageOrBitmap: + TkComputeAnchor(butPtr->anchor, tkwin, 0, 0, + butPtr->indicatorSpace + width, height, &x, &y); + x += butPtr->indicatorSpace; + + x += offset; + y += offset; + if (relief == TK_RELIEF_RAISED) { + x -= offset; + y -= offset; + } else if (relief == TK_RELIEF_SUNKEN) { + x += offset; + y += offset; + } + if (butPtr->image != NULL) { + if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) { + Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap, + x, y); + } else { + Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, + x, y); + } + } else { + XSetClipOrigin(butPtr->display, gc, x, y); + XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0, + (unsigned int) width, (unsigned int) height, x, y, 1); + XSetClipOrigin(butPtr->display, gc, 0, 0); + } + y += height/2; + } else if (butPtr->bitmap != None) { + Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); + goto imageOrBitmap; + } else { + TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, + butPtr->indicatorSpace + butPtr->textWidth, butPtr->textHeight, + &x, &y); + + x += butPtr->indicatorSpace; + + x += offset; + y += offset; + 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 += butPtr->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. + * This code is complicated by the possible combinations of focus + * highlight and default rings. We draw the focus and highlight rings + * using the highlight border and highlight foreground color. + */ + + if (relief != TK_RELIEF_FLAT) { + int inset = butPtr->highlightWidth; + if (butPtr->defaultState == tkActiveUid) { + /* + * Draw the default ring with 2 pixels of space between the + * default ring and the button and the default ring and the + * focus ring. Note that we need to explicitly draw the space + * in the highlightBorder color to ensure that we overwrite any + * overflow text and/or a different button background color. + */ + + Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset, + inset, Tk_Width(tkwin) - 2*inset, + Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT); + inset += 2; + Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset, + inset, Tk_Width(tkwin) - 2*inset, + Tk_Height(tkwin) - 2*inset, 1, TK_RELIEF_SUNKEN); + inset++; + Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset, + inset, Tk_Width(tkwin) - 2*inset, + Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT); + + inset += 2; + } else if (butPtr->defaultState == tkNormalUid) { + /* + * Leave room for the default ring and write over any text or + * background color. + */ + + Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0, + 0, Tk_Width(tkwin), + Tk_Height(tkwin), 5, TK_RELIEF_FLAT); + inset += 5; + } + + /* + * Draw the button border. + */ + + 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(Tk_3DBorderColor(butPtr->highlightBorder), + pixmap); + } + + /* + * Make sure the focus ring shrink-wraps the actual button, not the + * padding space left for a default ring. + */ + + if (butPtr->defaultState == tkNormalUid) { + TkDrawInsetFocusHighlight(tkwin, gc, butPtr->highlightWidth, + pixmap, 5); + } else { + 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 +TkpComputeButtonGeometry(butPtr) + register TkButton *butPtr; /* Button whose geometry may have changed. */ +{ + int width, height, avgWidth; + Tk_FontMetrics fm; + + if (butPtr->highlightWidth < 0) { + butPtr->highlightWidth = 0; + } + butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth; + + /* + * Leave room for the default ring if needed. + */ + + if (butPtr->defaultState != tkDisabledUid) { + butPtr->inset += 5; + } + butPtr->indicatorSpace = 0; + if (butPtr->image != NULL) { + Tk_SizeOfImage(butPtr->image, &width, &height); + imageOrBitmap: + if (butPtr->width > 0) { + width = butPtr->width; + } + if (butPtr->height > 0) { + height = butPtr->height; + } + 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; + } + } + } else if (butPtr->bitmap != None) { + Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); + goto imageOrBitmap; + } else { + Tk_FreeTextLayout(butPtr->textLayout); + butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont, + butPtr->text, -1, butPtr->wrapLength, butPtr->justify, 0, + &butPtr->textWidth, &butPtr->textHeight); + + width = butPtr->textWidth; + height = butPtr->textHeight; + avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1); + Tk_GetFontMetrics(butPtr->tkfont, &fm); + + if (butPtr->width > 0) { + width = butPtr->width * avgWidth; + } + if (butPtr->height > 0) { + height = butPtr->height * fm.linespace; + } + if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { + butPtr->indicatorDiameter = fm.linespace; + if (butPtr->type == TYPE_CHECK_BUTTON) { + butPtr->indicatorDiameter = (80*butPtr->indicatorDiameter)/100; + } + butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth; + } + } + + /* + * When issuing the geometry request, add extra space for the indicator, + * if any, and for the border and padding, plus two extra pixels so the + * display can be offset by 1 pixel in either direction for the raised + * or lowered effect. + */ + + if ((butPtr->image == NULL) && (butPtr->bitmap == None)) { + width += 2*butPtr->padX; + height += 2*butPtr->padY; + } + if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) { + width += 2; + height += 2; + } + Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace + + 2*butPtr->inset), (int) (height + 2*butPtr->inset)); + Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset); +} |