/* * tkUnixMenubu.c -- * * This file implements the Unix specific portion of the * menubutton widget. * * Copyright (c) 1996-1997 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. * * RCS: @(#) $Id: tkUnixMenubu.c,v 1.6.4.1 2003/11/17 23:29:36 hobbs Exp $ */ #include "tkMenubutton.h" /* * The structure below defines menubutton class behavior by means of * procedures that can be invoked from generic window code. */ Tk_ClassProcs tkpMenubuttonClass = { sizeof(Tk_ClassProcs), /* size */ TkMenuButtonWorldChanged, /* worldChangedProc */ }; /* *---------------------------------------------------------------------- * * TkpCreateMenuButton -- * * Allocate a new TkMenuButton structure. * * Results: * Returns a newly allocated TkMenuButton structure. * * Side effects: * Registers an event handler for the widget. * *---------------------------------------------------------------------- */ TkMenuButton * TkpCreateMenuButton(tkwin) Tk_Window tkwin; { return (TkMenuButton *)ckalloc(sizeof(TkMenuButton)); } /* *---------------------------------------------------------------------- * * TkpDisplayMenuButton -- * * This procedure is invoked to display a menubutton widget. * * Results: * None. * * Side effects: * Commands are output to X to display the menubutton in its * current mode. * *---------------------------------------------------------------------- */ void TkpDisplayMenuButton(clientData) ClientData clientData; /* Information about widget. */ { register TkMenuButton *mbPtr = (TkMenuButton *) clientData; GC gc; Tk_3DBorder border; Pixmap pixmap; int x = 0; /* Initialization needed only to stop * compiler warning. */ int y = 0; register Tk_Window tkwin = mbPtr->tkwin; int width, height, fullWidth, fullHeight; int textXOffset, textYOffset; int imageWidth, imageHeight; int imageXOffset, imageYOffset; /* image information that will be used to * restrict disabled pixmap as well */ int haveImage = 0, haveText = 0; mbPtr->flags &= ~REDRAW_PENDING; if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } if ((mbPtr->state == STATE_DISABLED) && (mbPtr->disabledFg != NULL)) { gc = mbPtr->disabledGC; border = mbPtr->normalBorder; } else if ((mbPtr->state == STATE_ACTIVE) && !Tk_StrictMotif(mbPtr->tkwin)) { gc = mbPtr->activeTextGC; border = mbPtr->activeBorder; } else { gc = mbPtr->normalTextGC; border = mbPtr->normalBorder; } if (mbPtr->image != None) { Tk_SizeOfImage(mbPtr->image, &width, &height); haveImage = 1; } else if (mbPtr->bitmap != None) { Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height); haveImage = 1; } imageWidth = width; imageHeight = height; haveText = (mbPtr->textWidth != 0 && mbPtr->textHeight != 0); /* * In order to avoid screen flashes, this procedure redraws * the menu 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(mbPtr->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); imageXOffset = 0; imageYOffset = 0; textXOffset = 0; textYOffset = 0; fullWidth = 0; fullHeight = 0; if (mbPtr->compound != COMPOUND_NONE && haveImage && haveText) { switch ((enum compound) mbPtr->compound) { case COMPOUND_TOP: case COMPOUND_BOTTOM: { /* Image is above or below text */ if (mbPtr->compound == COMPOUND_TOP) { textYOffset = height + mbPtr->padY; } else { imageYOffset = mbPtr->textHeight + mbPtr->padY; } fullHeight = height + mbPtr->textHeight + mbPtr->padY; fullWidth = (width > mbPtr->textWidth ? width : mbPtr->textWidth); textXOffset = (fullWidth - mbPtr->textWidth)/2; imageXOffset = (fullWidth - width)/2; break; } case COMPOUND_LEFT: case COMPOUND_RIGHT: { /* Image is left or right of text */ if (mbPtr->compound == COMPOUND_LEFT) { textXOffset = width + mbPtr->padX; } else { imageXOffset = mbPtr->textWidth + mbPtr->padX; } fullWidth = mbPtr->textWidth + mbPtr->padX + width; fullHeight = (height > mbPtr->textHeight ? height : mbPtr->textHeight); textYOffset = (fullHeight - mbPtr->textHeight)/2; imageYOffset = (fullHeight - height)/2; break; } case COMPOUND_CENTER: { /* Image and text are superimposed */ fullWidth = (width > mbPtr->textWidth ? width : mbPtr->textWidth); fullHeight = (height > mbPtr->textHeight ? height : mbPtr->textHeight); textXOffset = (fullWidth - mbPtr->textWidth)/2; imageXOffset = (fullWidth - width)/2; textYOffset = (fullHeight - mbPtr->textHeight)/2; imageYOffset = (fullHeight - height)/2; break; } case COMPOUND_NONE: {break;} } TkComputeAnchor(mbPtr->anchor, tkwin, 0, 0, mbPtr->indicatorWidth + fullWidth, fullHeight, &x, &y); imageXOffset += x; imageYOffset += y; if (mbPtr->image != NULL) { Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap, imageXOffset, imageYOffset); } else if (mbPtr->bitmap != None) { XSetClipOrigin(mbPtr->display, gc, imageXOffset, imageYOffset); XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap, gc, 0, 0, (unsigned) width, (unsigned) height, imageXOffset, imageYOffset, 1); XSetClipOrigin(mbPtr->display, gc, 0, 0); } Tk_DrawTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1); Tk_UnderlineTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout, x + textXOffset, y + textYOffset, mbPtr->underline); } else if (haveImage) { TkComputeAnchor(mbPtr->anchor, tkwin, 0, 0, width + mbPtr->indicatorWidth, height, &x, &y); imageXOffset += x; imageYOffset += y; if (mbPtr->image != NULL) { Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap, imageXOffset, imageYOffset); } else if (mbPtr->bitmap != None) { XSetClipOrigin(mbPtr->display, gc, x, y); XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap, gc, 0, 0, (unsigned) width, (unsigned) height, x, y, 1); XSetClipOrigin(mbPtr->display, gc, 0, 0); } } else { TkComputeAnchor(mbPtr->anchor, tkwin, mbPtr->padX, mbPtr->padY, mbPtr->textWidth + mbPtr->indicatorWidth, mbPtr->textHeight, &x, &y); Tk_DrawTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1); Tk_UnderlineTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout, x + textXOffset, y + textYOffset, mbPtr->underline); } /* * If the menu button is disabled with a stipple rather than a special * foreground color, generate the stippled effect. */ if ((mbPtr->state == STATE_DISABLED) && ((mbPtr->disabledFg == NULL) || (mbPtr->image != NULL))) { /* * Stipple the whole button if no disabledFg was specified, * otherwise restrict stippling only to displayed image */ if (mbPtr->disabledFg == NULL) { XFillRectangle(mbPtr->display, pixmap, mbPtr->stippleGC, mbPtr->inset, mbPtr->inset, (unsigned) (Tk_Width(tkwin) - 2*mbPtr->inset), (unsigned) (Tk_Height(tkwin) - 2*mbPtr->inset)); } else { XFillRectangle(mbPtr->display, pixmap, mbPtr->stippleGC, imageXOffset, imageYOffset, (unsigned) imageWidth, (unsigned) imageHeight); } } /* * Draw the cascade indicator for the menu button on the * right side of the window, if desired. */ if (mbPtr->indicatorOn) { int borderWidth; borderWidth = (mbPtr->indicatorHeight+1)/3; if (borderWidth < 1) { borderWidth = 1; } /*y += mbPtr->textHeight / 2;*/ Tk_Fill3DRectangle(tkwin, pixmap, border, Tk_Width(tkwin) - mbPtr->inset - mbPtr->indicatorWidth + mbPtr->indicatorHeight, ((int) (Tk_Height(tkwin) - mbPtr->indicatorHeight))/2, mbPtr->indicatorWidth - 2*mbPtr->indicatorHeight, mbPtr->indicatorHeight, borderWidth, TK_RELIEF_RAISED); } /* * Draw the border and traversal highlight last. This way, if the * menu button's contents overflow onto the border they'll be covered * up by the border. */ if (mbPtr->relief != TK_RELIEF_FLAT) { Tk_Draw3DRectangle(tkwin, pixmap, border, mbPtr->highlightWidth, mbPtr->highlightWidth, Tk_Width(tkwin) - 2*mbPtr->highlightWidth, Tk_Height(tkwin) - 2*mbPtr->highlightWidth, mbPtr->borderWidth, mbPtr->relief); } if (mbPtr->highlightWidth != 0) { GC gc; if (mbPtr->flags & GOT_FOCUS) { gc = Tk_GCForColor(mbPtr->highlightColorPtr, pixmap); } else { gc = Tk_GCForColor(mbPtr->highlightBgColorPtr, pixmap); } Tk_DrawFocusHighlight(tkwin, gc, mbPtr->highlightWidth, pixmap); } /* * Copy the information from the off-screen pixmap onto the screen, * then delete the pixmap. */ XCopyArea(mbPtr->display, pixmap, Tk_WindowId(tkwin), mbPtr->normalTextGC, 0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0); Tk_FreePixmap(mbPtr->display, pixmap); } /* *---------------------------------------------------------------------- * * TkpDestroyMenuButton -- * * Free data structures associated with the menubutton control. * * Results: * None. * * Side effects: * Restores the default control state. * *---------------------------------------------------------------------- */ void TkpDestroyMenuButton(mbPtr) TkMenuButton *mbPtr; { } /* *---------------------------------------------------------------------- * * TkpComputeMenuButtonGeometry -- * * After changes in a menu button's text or bitmap, this procedure * recomputes the menu button's geometry and passes this information * along to the geometry manager for the window. * * Results: * None. * * Side effects: * The menu button's window may change size. * *---------------------------------------------------------------------- */ void TkpComputeMenuButtonGeometry(mbPtr) TkMenuButton *mbPtr; /* Widget record for menu button. */ { int width, height, mm, pixels; int avgWidth, txtWidth, txtHeight; int haveImage = 0, haveText = 0; Tk_FontMetrics fm; mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth; width = 0; height = 0; txtWidth = 0; txtHeight = 0; avgWidth = 0; if (mbPtr->image != None) { Tk_SizeOfImage(mbPtr->image, &width, &height); haveImage = 1; } else if (mbPtr->bitmap != None) { Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height); haveImage = 1; } if (haveImage == 0 || mbPtr->compound != COMPOUND_NONE) { Tk_FreeTextLayout(mbPtr->textLayout); mbPtr->textLayout = Tk_ComputeTextLayout(mbPtr->tkfont, mbPtr->text, -1, mbPtr->wrapLength, mbPtr->justify, 0, &mbPtr->textWidth, &mbPtr->textHeight); txtWidth = mbPtr->textWidth; txtHeight = mbPtr->textHeight; avgWidth = Tk_TextWidth(mbPtr->tkfont, "0", 1); Tk_GetFontMetrics(mbPtr->tkfont, &fm); haveText = (txtWidth != 0 && txtHeight != 0); } /* * If the menubutton is compound (ie, it shows both an image and text), * the new geometry is a combination of the image and text geometry. * We only honor the compound bit if the menubutton has both text and * an image, because otherwise it is not really a compound menubutton. */ if (mbPtr->compound != COMPOUND_NONE && haveImage && haveText) { switch ((enum compound) mbPtr->compound) { case COMPOUND_TOP: case COMPOUND_BOTTOM: { /* Image is above or below text */ height += txtHeight + mbPtr->padY; width = (width > txtWidth ? width : txtWidth); break; } case COMPOUND_LEFT: case COMPOUND_RIGHT: { /* Image is left or right of text */ width += txtWidth + mbPtr->padX; height = (height > txtHeight ? height : txtHeight); break; } case COMPOUND_CENTER: { /* Image and text are superimposed */ width = (width > txtWidth ? width : txtWidth); height = (height > txtHeight ? height : txtHeight); break; } case COMPOUND_NONE: {break;} } if (mbPtr->width > 0) { width = mbPtr->width; } if (mbPtr->height > 0) { height = mbPtr->height; } width += 2*mbPtr->padX; height += 2*mbPtr->padY; } else { if (haveImage) { if (mbPtr->width > 0) { width = mbPtr->width; } if (mbPtr->height > 0) { height = mbPtr->height; } } else { width = txtWidth; height = txtHeight; if (mbPtr->width > 0) { width = mbPtr->width * avgWidth; } if (mbPtr->height > 0) { height = mbPtr->height * fm.linespace; } } } if (! haveImage) { width += 2*mbPtr->padX; height += 2*mbPtr->padY; } if (mbPtr->indicatorOn) { mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin)); pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin)); mbPtr->indicatorHeight= (INDICATOR_HEIGHT * pixels)/(10*mm); mbPtr->indicatorWidth = (INDICATOR_WIDTH * pixels)/(10*mm) + 2*mbPtr->indicatorHeight; width += mbPtr->indicatorWidth; } else { mbPtr->indicatorHeight = 0; mbPtr->indicatorWidth = 0; } Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset), (int) (height + 2*mbPtr->inset)); Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset); }