/* * tkUnixMenu.c -- * * This module implements the UNIX platform-specific features of menus. * * Copyright © 1996-1998 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tkUnixInt.h" #include "tkMenu.h" /* * Constants used for menu drawing. */ #define MENU_MARGIN_WIDTH 2 #define MENU_DIVIDER_HEIGHT 2 /* * Platform specific flags for Unix. */ #define ENTRY_HELP_MENU ENTRY_PLATFORM_FLAG1 /* * Shared with button widget. */ MODULE_SCOPE void TkpDrawCheckIndicator(Tk_Window tkwin, Display *display, Drawable d, int x, int y, Tk_3DBorder bgBorder, XColor *indicatorColor, XColor *selectColor, XColor *disColor, int on, int disabled, int mode); /* * Indicator Draw Modes */ #define CHECK_BUTTON 0 #define CHECK_MENU 1 #define RADIO_BUTTON 2 #define RADIO_MENU 3 /* * Procedures used internally. */ static void SetHelpMenu(TkMenu *menuPtr); static void DrawMenuEntryAccelerator(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, Tk_3DBorder activeBorder, Tk_3DBorder bgBorder, int x, int y, int width, int height, int drawArrow); static void DrawMenuEntryBackground(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d, Tk_3DBorder activeBorder, Tk_3DBorder bgBorder, int x, int y, int width, int heigth); static void DrawMenuEntryIndicator(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d, Tk_3DBorder border, XColor *indicatorColor, XColor *disableColor, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y, int width, int height); static void DrawMenuEntryLabel(TkMenu * menuPtr, TkMenuEntry *mePtr, Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y, int width, int height); static void DrawMenuSeparator(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y, int width, int height); static void DrawTearoffEntry(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y, int width, int height); static void DrawMenuUnderline(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y, int width, int height); static void GetMenuAccelGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr, int *heightPtr); static void GetMenuLabelGeometry(TkMenuEntry *mePtr, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr, int *heightPtr); static void GetMenuIndicatorGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr, int *heightPtr); static void GetMenuSeparatorGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr, int *heightPtr); static void GetTearoffEntryGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr, int *heightPtr); /* *---------------------------------------------------------------------- * * TkpNewMenu -- * * Gets the platform-specific piece of the menu. Invoked during idle * after the generic part of the menu has been created. * * Results: * Standard TCL error. * * Side effects: * Allocates any platform specific allocations and places them in the * platformData field of the menuPtr. * *---------------------------------------------------------------------- */ int TkpNewMenu( TkMenu *menuPtr) { SetHelpMenu(menuPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * TkpDestroyMenu -- * * Destroys platform-specific menu structures. Called when the generic * menu structure is destroyed for the menu. * * Results: * None. * * Side effects: * All platform-specific allocations are freed up. * *---------------------------------------------------------------------- */ void TkpDestroyMenu( TCL_UNUSED(TkMenu *)) { /* * Nothing to do. */ } /* *---------------------------------------------------------------------- * * TkpDestroyMenuEntry -- * * Cleans up platform-specific menu entry items. Called when entry is * destroyed in the generic code. * * Results: * None. * * Side effects: * All platform specific allocations are freed up. * *---------------------------------------------------------------------- */ void TkpDestroyMenuEntry( TCL_UNUSED(TkMenuEntry *)) { /* * Nothing to do. */ } /* *---------------------------------------------------------------------- * * TkpConfigureMenuEntry -- * * Processes configuration options for menu entries. Called when the * generic options are processed for the menu. * * Results: * Returns standard TCL result. If TCL_ERROR is returned, then the * interp's result contains an error message. * * Side effects: * Configuration information get set for mePtr; old resources get freed, * if any need it. * *---------------------------------------------------------------------- */ int TkpConfigureMenuEntry( TkMenuEntry *mePtr)/* Information about menu entry; may or may * not already have values for some fields. */ { /* * If this is a cascade menu, and the child menu exists, check to see if * the child menu is a help menu. */ if ((mePtr->type == CASCADE_ENTRY) && (mePtr->namePtr != NULL)) { TkMenuReferences *menuRefPtr; menuRefPtr = TkFindMenuReferencesObj(mePtr->menuPtr->interp, mePtr->namePtr); if ((menuRefPtr != NULL) && (menuRefPtr->menuPtr != NULL)) { SetHelpMenu(menuRefPtr->menuPtr); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * TkpMenuNewEntry -- * * Called when a new entry is created in a menu. Fills in platform * specific data for the entry. The platformEntryData field is used to * store the indicator diameter for radio button and check box entries. * * Results: * Standard TCL error. * * Side effects: * None on Unix. * *---------------------------------------------------------------------- */ int TkpMenuNewEntry( TCL_UNUSED(TkMenuEntry *)) { return TCL_OK; } /* *---------------------------------------------------------------------- * * TkpSetWindowMenuBar -- * * Sets up the menu as a menubar in the given window. * * Results: * None. * * Side effects: * Recomputes geometry of given window. * *---------------------------------------------------------------------- */ void TkpSetWindowMenuBar( Tk_Window tkwin, /* The window we are setting */ TkMenu *menuPtr) /* The menu we are setting */ { if (menuPtr == NULL) { TkUnixSetMenubar(tkwin, NULL); } else { TkUnixSetMenubar(tkwin, menuPtr->tkwin); } } /* *---------------------------------------------------------------------- * * TkpSetMainMenuBar -- * * Called when a toplevel widget is brought to front. On the Macintosh, * sets up the menubar that goes accross the top of the main monitor. On * other platforms, nothing is necessary. * * Results: * None. * * Side effects: * Recompute geometry of given window. * *---------------------------------------------------------------------- */ void Tk_SetMainMenubar( TCL_UNUSED(Tcl_Interp *), TCL_UNUSED(Tk_Window), TCL_UNUSED(const char *)) { /* * Nothing to do. */ } /* *---------------------------------------------------------------------- * * GetMenuIndicatorGeometry -- * * Fills out the geometry of the indicator in a menu item. Note that the * mePtr->height field must have already been filled in by * GetMenuLabelGeometry since this height depends on the label height. * * Results: * widthPtr and heightPtr point to the new geometry values. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void GetMenuIndicatorGeometry( TkMenu *menuPtr, /* The menu we are drawing. */ TkMenuEntry *mePtr, /* The entry we are interested in. */ TCL_UNUSED(Tk_Font), /* The precalculated font */ TCL_UNUSED(const Tk_FontMetrics *), /* The precalculated metrics */ int *widthPtr, /* The resulting width */ int *heightPtr) /* The resulting height */ { int borderWidth; if ((mePtr->type == CHECK_BUTTON_ENTRY) || (mePtr->type == RADIO_BUTTON_ENTRY)) { if (!mePtr->hideMargin && mePtr->indicatorOn) { if ((mePtr->image != NULL) || (mePtr->bitmapPtr != NULL)) { *widthPtr = (14 * mePtr->height) / 10; *heightPtr = mePtr->height; if (mePtr->type == CHECK_BUTTON_ENTRY) { mePtr->platformEntryData = (TkMenuPlatformEntryData) INT2PTR((65 * mePtr->height) / 100); } else { mePtr->platformEntryData = (TkMenuPlatformEntryData) INT2PTR((75 * mePtr->height) / 100); } } else { *widthPtr = *heightPtr = mePtr->height; if (mePtr->type == CHECK_BUTTON_ENTRY) { mePtr->platformEntryData = (TkMenuPlatformEntryData) INT2PTR((80 * mePtr->height) / 100); } else { mePtr->platformEntryData = (TkMenuPlatformEntryData) INT2PTR(mePtr->height); } } } else { Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthObj, &borderWidth); *heightPtr = 0; *widthPtr = borderWidth; } } else { Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthObj, &borderWidth); *heightPtr = 0; *widthPtr = borderWidth; } } /* *---------------------------------------------------------------------- * * GetMenuAccelGeometry -- * * Get the geometry of the accelerator area of a menu item. * * Results: * heightPtr and widthPtr are set. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void GetMenuAccelGeometry( TkMenu *menuPtr, /* The menu was are drawing */ TkMenuEntry *mePtr, /* The entry we are getting the geometry for */ Tk_Font tkfont, /* The precalculated font */ const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ int *widthPtr, /* The width of the acclerator area */ int *heightPtr) /* The height of the accelerator area */ { double scalingLevel = TkScalingLevel(menuPtr->tkwin); *heightPtr = fmPtr->linespace; if (mePtr->type == CASCADE_ENTRY) { *widthPtr = 2 * CASCADE_ARROW_WIDTH * scalingLevel; } else if ((menuPtr->menuType != MENUBAR) && (mePtr->accelPtr != NULL)) { const char *accel = Tcl_GetString(mePtr->accelPtr); *widthPtr = Tk_TextWidth(tkfont, accel, mePtr->accelLength); } else { *widthPtr = 0; } } /* *---------------------------------------------------------------------- * * DrawMenuEntryBackground -- * * This procedure draws the background part of a menu. * * Results: * None. * * Side effects: * Commands are output to X to display the menu in its current mode. * *---------------------------------------------------------------------- */ static void DrawMenuEntryBackground( TkMenu *menuPtr, /* The menu we are drawing */ TkMenuEntry *mePtr, /* The entry we are drawing. */ Drawable d, /* The drawable we are drawing into */ Tk_3DBorder activeBorder, /* The border for an active item */ Tk_3DBorder bgBorder, /* The background border */ int x, /* Left coordinate of entry rect */ int y, /* Right coordinate of entry rect */ int width, /* Width of entry rect */ int height) /* Height of entry rect */ { if (mePtr->state == ENTRY_ACTIVE) { int relief; int activeBorderWidth; bgBorder = activeBorder; if ((menuPtr->menuType == MENUBAR) && ((menuPtr->postedCascade == NULL) || (menuPtr->postedCascade != mePtr))) { relief = TK_RELIEF_FLAT; } else { Tk_GetReliefFromObj(NULL, menuPtr->activeReliefPtr, &relief); } Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, &activeBorderWidth); Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height, activeBorderWidth, relief); } else { Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height, 0, TK_RELIEF_FLAT); } } /* *---------------------------------------------------------------------- * * DrawMenuEntryAccelerator -- * * This procedure draws the background part of a menu. * * Results: * None. * * Side effects: * Commands are output to X to display the menu in its current mode. * *---------------------------------------------------------------------- */ static void DrawMenuEntryAccelerator( TkMenu *menuPtr, /* The menu we are drawing */ TkMenuEntry *mePtr, /* The entry we are drawing */ Drawable d, /* The drawable we are drawing into */ GC gc, /* The precalculated gc to draw with */ Tk_Font tkfont, /* The precalculated font */ const Tk_FontMetrics *fmPtr,/* The precalculated metrics */ Tk_3DBorder activeBorder, /* The border for an active item */ Tk_3DBorder bgBorder, /* The background border */ int x, /* Left coordinate of entry rect */ int y, /* Top coordinate of entry rect */ int width, /* Width of entry */ int height, /* Height of entry */ int drawArrow) /* Whether or not to draw arrow. */ { XPoint points[3]; int borderWidth, activeBorderWidth; int arrowWidth = CASCADE_ARROW_WIDTH, arrowHeight = CASCADE_ARROW_HEIGHT; double scalingLevel = TkScalingLevel(menuPtr->tkwin); /* * Draw accelerator or cascade arrow. */ if (menuPtr->menuType == MENUBAR) { return; } Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthObj, &borderWidth); Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, &activeBorderWidth); if ((mePtr->type == CASCADE_ENTRY) && drawArrow) { arrowWidth *= scalingLevel; arrowHeight *= scalingLevel; points[0].x = x + width - borderWidth - activeBorderWidth - arrowWidth; points[0].y = y + (height - arrowHeight)/2; points[1].x = points[0].x; points[1].y = points[0].y + arrowHeight; points[2].x = points[0].x + arrowWidth; points[2].y = points[0].y + arrowHeight/2; Tk_Fill3DPolygon(menuPtr->tkwin, d, (mePtr->state == ENTRY_ACTIVE) ? activeBorder : bgBorder, points, 3, DECORATION_BORDER_WIDTH, (menuPtr->postedCascade == mePtr) ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED); } else if (mePtr->accelPtr != NULL) { const char *accel = Tcl_GetString(mePtr->accelPtr); int left = x + mePtr->labelWidth + activeBorderWidth + mePtr->indicatorSpace; if (menuPtr->menuType == MENUBAR) { left += 5; } Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel, mePtr->accelLength, left, (y + (height + fmPtr->ascent - fmPtr->descent) / 2)); } } /* *---------------------------------------------------------------------- * * DrawMenuEntryIndicator -- * * This procedure draws the background part of a menu. * * Results: * None. * * Side effects: * Commands are output to X to display the menu in its current mode. * *---------------------------------------------------------------------- */ static void DrawMenuEntryIndicator( TkMenu *menuPtr, /* The menu we are drawing */ TkMenuEntry *mePtr, /* The entry we are drawing */ Drawable d, /* The drawable to draw into */ Tk_3DBorder border, /* The background color */ XColor *indicatorColor, /* The color to draw indicators with */ XColor *disableColor, /* The color use use when disabled */ TCL_UNUSED(Tk_Font), /* The font to draw with */ TCL_UNUSED(const Tk_FontMetrics *), /* The font metrics of the font */ int x, /* The left of the entry rect */ int y, /* The top of the entry rect */ TCL_UNUSED(int), /* Width of menu entry */ int height) /* Height of menu entry */ { /* * Draw check-button indicator. */ if ((mePtr->type == CHECK_BUTTON_ENTRY) && mePtr->indicatorOn) { int top, left, activeBorderWidth; int disabled = (mePtr->state == ENTRY_DISABLED); XColor *bg; Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, &activeBorderWidth); bg = Tk_3DBorderColor(border); top = y + height/2; left = x + activeBorderWidth + DECORATION_BORDER_WIDTH + mePtr->indicatorSpace/2; TkpDrawCheckIndicator(menuPtr->tkwin, menuPtr->display, d, left, top, border, indicatorColor, bg, disableColor, (mePtr->entryFlags & ENTRY_SELECTED), disabled, CHECK_MENU); } /* * Draw radio-button indicator. */ if ((mePtr->type == RADIO_BUTTON_ENTRY) && mePtr->indicatorOn) { int top, left, activeBorderWidth; int disabled = (mePtr->state == ENTRY_DISABLED); XColor *bg; Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, &activeBorderWidth); bg = Tk_3DBorderColor(border); top = y + height/2; left = x + activeBorderWidth + DECORATION_BORDER_WIDTH + mePtr->indicatorSpace/2; TkpDrawCheckIndicator(menuPtr->tkwin, menuPtr->display, d, left, top, border, indicatorColor, bg, disableColor, (mePtr->entryFlags & ENTRY_SELECTED), disabled, RADIO_MENU); } } /* *---------------------------------------------------------------------- * * DrawMenuSeparator -- * * This procedure draws a separator menu item. * * Results: * None. * * Side effects: * Commands are output to X to display the menu in its current mode. * *---------------------------------------------------------------------- */ static void DrawMenuSeparator( TkMenu *menuPtr, /* The menu we are drawing */ TCL_UNUSED(TkMenuEntry *), /* The entry we are drawing */ Drawable d, /* The drawable we are using */ TCL_UNUSED(GC), /* The gc to draw into */ TCL_UNUSED(Tk_Font), /* The font to draw with */ TCL_UNUSED(const Tk_FontMetrics *), /* The font metrics from the font */ int x, int y, int width, int height) { XPoint points[2]; Tk_3DBorder border; if (menuPtr->menuType == MENUBAR) { return; } points[0].x = x; points[0].y = y + height/2; points[1].x = x + width - 1; points[1].y = points[0].y; border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1, TK_RELIEF_RAISED); } /* *---------------------------------------------------------------------- * * DrawMenuEntryLabel -- * * This procedure draws the label part of a menu. * * Results: * None. * * Side effects: * Commands are output to X to display the menu in its current mode. * *---------------------------------------------------------------------- */ static void DrawMenuEntryLabel( TkMenu *menuPtr, /* The menu we are drawing. */ TkMenuEntry *mePtr, /* The entry we are drawing. */ Drawable d, /* What we are drawing into. */ GC gc, /* The gc we are drawing into.*/ Tk_Font tkfont, /* The precalculated font. */ const Tk_FontMetrics *fmPtr,/* The precalculated font metrics. */ int x, /* Left edge. */ int y, /* Top edge. */ int width, /* width of entry. */ int height) /* height of entry. */ { int indicatorSpace = mePtr->indicatorSpace; int activeBorderWidth, leftEdge, imageHeight, imageWidth; int textHeight = 0, textWidth = 0; /* stop GCC warning */ int haveImage = 0, haveText = 0; int imageXOffset = 0, imageYOffset = 0; int textXOffset = 0, textYOffset = 0; Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, &activeBorderWidth); leftEdge = x + indicatorSpace + activeBorderWidth; if (menuPtr->menuType == MENUBAR) { leftEdge += 5; } /* * Work out what we will need to draw first. */ if (mePtr->image != NULL) { Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight); haveImage = 1; } else if (mePtr->bitmapPtr != NULL) { Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); Tk_SizeOfBitmap(menuPtr->display, bitmap, &imageWidth, &imageHeight); haveImage = 1; } if (!haveImage || (mePtr->compound != COMPOUND_NONE)) { if (mePtr->labelLength > 0) { const char *label = Tcl_GetString(mePtr->labelPtr); textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength); textHeight = fmPtr->linespace; haveText = 1; } } /* * Now work out what the relative positions are. */ if (haveImage && haveText) { int fullWidth = (imageWidth > textWidth ? imageWidth : textWidth); switch ((enum compound) mePtr->compound) { case COMPOUND_TOP: textXOffset = (fullWidth - textWidth)/2; textYOffset = imageHeight/2 + 2; imageXOffset = (fullWidth - imageWidth)/2; imageYOffset = -textHeight/2; break; case COMPOUND_BOTTOM: textXOffset = (fullWidth - textWidth)/2; textYOffset = -imageHeight/2; imageXOffset = (fullWidth - imageWidth)/2; imageYOffset = textHeight/2 + 2; break; case COMPOUND_LEFT: /* * Position image in the indicator space to the left of the * entries, unless this entry is a radio|check button because then * the indicator space will be used. */ textXOffset = imageWidth + 2; textYOffset = 0; imageXOffset = 0; imageYOffset = 0; if ((mePtr->type != CHECK_BUTTON_ENTRY) && (mePtr->type != RADIO_BUTTON_ENTRY)) { textXOffset -= indicatorSpace; if (textXOffset < 0) { textXOffset = 0; } imageXOffset = -indicatorSpace; } break; case COMPOUND_RIGHT: textXOffset = 0; textYOffset = 0; imageXOffset = textWidth + 2; imageYOffset = 0; break; case COMPOUND_CENTER: textXOffset = (fullWidth - textWidth)/2; textYOffset = 0; imageXOffset = (fullWidth - imageWidth)/2; imageYOffset = 0; break; case COMPOUND_NONE: break; } } else { textXOffset = 0; textYOffset = 0; imageXOffset = 0; imageYOffset = 0; } /* * Draw label and/or bitmap or image for entry. */ if (mePtr->image != NULL) { if ((mePtr->selectImage != NULL) && (mePtr->entryFlags & ENTRY_SELECTED)) { Tk_RedrawImage(mePtr->selectImage, 0, 0, imageWidth, imageHeight, d, leftEdge + imageXOffset, (int) (y + (mePtr->height-imageHeight)/2 + imageYOffset)); } else { Tk_RedrawImage(mePtr->image, 0, 0, imageWidth, imageHeight, d, leftEdge + imageXOffset, (int) (y + (mePtr->height-imageHeight)/2 + imageYOffset)); } } else if (mePtr->bitmapPtr != NULL) { Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); XCopyPlane(menuPtr->display, bitmap, d, gc, 0, 0, (unsigned) imageWidth, (unsigned) imageHeight, leftEdge + imageXOffset, (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset), 1); } if ((mePtr->compound != COMPOUND_NONE) || !haveImage) { int baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2; if (mePtr->labelLength > 0) { const char *label = Tcl_GetString(mePtr->labelPtr); Tk_DrawChars(menuPtr->display, d, gc, tkfont, label, mePtr->labelLength, leftEdge + textXOffset, baseline + textYOffset); DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, x + textXOffset, y + textYOffset, width, height); } } if (mePtr->state == ENTRY_DISABLED) { if (menuPtr->disabledFgPtr == NULL) { XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y, (unsigned) width, (unsigned) height); } else if ((mePtr->image != NULL) && (menuPtr->disabledImageGC != NULL)) { XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC, leftEdge + imageXOffset, (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset), (unsigned) imageWidth, (unsigned) imageHeight); } } } /* *---------------------------------------------------------------------- * * DrawMenuUnderline -- * * On appropriate platforms, draw the underline character for the menu. * * Results: * None. * * Side effects: * Commands are output to X to display the menu in its current mode. * *---------------------------------------------------------------------- */ static void DrawMenuUnderline( TkMenu *menuPtr, /* The menu to draw into */ TkMenuEntry *mePtr, /* The entry we are drawing */ Drawable d, /* What we are drawing into */ GC gc, /* The gc to draw into */ Tk_Font tkfont, /* The precalculated font */ const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ int x, int y, TCL_UNUSED(int), int height) { if (mePtr->labelPtr != NULL) { int len; len = Tcl_GetCharLength(mePtr->labelPtr); if (mePtr->underline < len && mePtr->underline >= -len) { int activeBorderWidth, leftEdge, ch; const char *label, *start, *end; label = Tcl_GetString(mePtr->labelPtr); start = Tcl_UtfAtIndex(label, (mePtr->underline < 0) ? mePtr->underline + len : mePtr->underline); end = start + Tcl_UtfToUniChar(start, &ch); Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, &activeBorderWidth); leftEdge = x + mePtr->indicatorSpace + activeBorderWidth; if (menuPtr->menuType == MENUBAR) { leftEdge += 5; } Tk_UnderlineChars(menuPtr->display, d, gc, tkfont, label, leftEdge, y + (height + fmPtr->ascent - fmPtr->descent) / 2, start - label, end - label); } } } /* *---------------------------------------------------------------------- * * TkpPostMenu -- * * Posts a menu on the screen so that the top left corner of the * specified entry is located at the point (x, y) in screen coordinates. * If the entry parameter is negative, the upper left corner of the * menu itself is placed at the point. * * Results: * None. * * Side effects: * The menu is posted and handled. * *---------------------------------------------------------------------- */ int TkpPostMenu( Tcl_Interp *interp, TkMenu *menuPtr, int x, int y, Tcl_Size index) { return TkpPostTearoffMenu(interp, menuPtr, x, y, index); } /* *---------------------------------------------------------------------- * * TkpPostTearoffMenu -- * * Posts a tearoff menu on the screen so that the top left corner of the * specified entry is located at the point (x, y) in screen coordinates. * If the index parameter is negative, the upper left corner of the menu * itself is placed at the point. On unix this is called when posting * any menu. Adjusts the menu's position so that it fits on the screen, * and maps and raises the menu. * * Results: * Returns a standard Tcl Error. * * Side effects: * The menu is posted. * *---------------------------------------------------------------------- */ int TkpPostTearoffMenu( TCL_UNUSED(Tcl_Interp *), /* The interpreter of the menu */ TkMenu *menuPtr, /* The menu we are posting */ int x, int y, Tcl_Size index) /* The root X,Y coordinates where the * specified entry will be posted */ { int vRootX, vRootY, vRootWidth, vRootHeight; int result; TkActivateMenuEntry(menuPtr, TCL_INDEX_NONE); TkRecomputeMenu(menuPtr); result = TkPostCommand(menuPtr); if (result != TCL_OK) { return result; } /* * The post commands could have deleted the menu, which means we are dead * and should go away. */ if (menuPtr->tkwin == NULL) { return TCL_OK; } /* * Adjust the menu y position so that the specified entry will be located * at the given coordinates. */ if (index >= menuPtr->numEntries) { index = menuPtr->numEntries - 1; } if (index >= 0) { y -= menuPtr->entries[index]->y; } /* * Adjust the position of the menu if necessary to keep it visible on the * screen. There are two special tricks to make this work right: * * 1. If a virtual root window manager is being used then the coordinates * are in the virtual root window of menuPtr's parent; since the menu * uses override-redirect mode it will be in the *real* root window for * the screen, so we have to map the coordinates from the virtual root * (if any) to the real root. Can't get the virtual root from the menu * itself (it will never be seen by the wm) so use its parent instead * (it would be better to have an an option that names a window to use * for this...). * 2. The menu may not have been mapped yet, so its current size might be * the default 1x1. To compute how much space it needs, use its * requested size, not its actual size. */ Tk_GetVRootGeometry(Tk_Parent(menuPtr->tkwin), &vRootX, &vRootY, &vRootWidth, &vRootHeight); vRootWidth -= Tk_ReqWidth(menuPtr->tkwin); if (x > vRootX + vRootWidth) { x = vRootX + vRootWidth; } if (x < vRootX) { x = vRootX; } vRootHeight -= Tk_ReqHeight(menuPtr->tkwin); if (y > vRootY + vRootHeight) { y = vRootY + vRootHeight; } if (y < vRootY) { y = vRootY; } Tk_MoveToplevelWindow(menuPtr->tkwin, x, y); if (!Tk_IsMapped(menuPtr->tkwin)) { Tk_MapWindow(menuPtr->tkwin); } TkWmRestackToplevel((TkWindow *) menuPtr->tkwin, Above, NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * GetMenuSeparatorGeometry -- * * Gets the width and height of the indicator area of a menu. * * Results: * widthPtr and heightPtr are set. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void GetMenuSeparatorGeometry( TCL_UNUSED(TkMenu *), /* The menu we are measuring */ TCL_UNUSED(TkMenuEntry *), /* The entry we are measuring */ TCL_UNUSED(Tk_Font), /* The precalculated font */ const Tk_FontMetrics *fmPtr, /* The precalcualted font metrics */ int *widthPtr, /* The resulting width */ int *heightPtr) /* The resulting height */ { *widthPtr = 0; *heightPtr = fmPtr->linespace; } /* *---------------------------------------------------------------------- * * GetTearoffEntryGeometry -- * * Gets the width and height of the indicator area of a menu. * * Results: * widthPtr and heightPtr are set. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void GetTearoffEntryGeometry( TkMenu *menuPtr, /* The menu we are drawing */ TCL_UNUSED(TkMenuEntry *), /* The entry we are measuring */ Tk_Font tkfont, /* The precalculated font */ const Tk_FontMetrics *fmPtr, /* The precalculated font metrics */ int *widthPtr, /* The resulting width */ int *heightPtr) /* The resulting height */ { if (menuPtr->menuType != MAIN_MENU) { *heightPtr = 0; *widthPtr = 0; } else { *heightPtr = fmPtr->linespace; *widthPtr = Tk_TextWidth(tkfont, "W", 1); } } /* *-------------------------------------------------------------- * * TkpComputeMenubarGeometry -- * * This procedure is invoked to recompute the size and layout of a menu * that is a menubar clone. * * Results: * None. * * Side effects: * Fields of menu entries are changed to reflect their current positions, * and the size of the menu window itself may be changed. * *-------------------------------------------------------------- */ void TkpComputeMenubarGeometry( TkMenu *menuPtr) /* Structure describing menu. */ { Tk_Font tkfont, menuFont; Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr; int width, height, x, y, currentRowHeight, maxWidth; Tcl_Size i, j, lastRowBreak; int maxWindowWidth, lastEntry; int activeBorderWidth, helpMenuIndex = -1; TkMenuEntry *mePtr; if (menuPtr->tkwin == NULL) { return; } Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, &activeBorderWidth); maxWidth = 0; if (menuPtr->numEntries == 0) { height = 0; } else { int borderWidth; maxWindowWidth = Tk_Width(menuPtr->tkwin); if (maxWindowWidth == 1) { maxWindowWidth = 0x7FFFFFF; } currentRowHeight = 0; Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthObj, &borderWidth); x = y = borderWidth; lastRowBreak = 0; /* * On the Mac especially, getting font metrics can be quite slow, so * we want to do it intelligently. We are going to precalculate them * and pass them down to all of the measuring and drawing routines. * We will measure the font metrics of the menu once, and if an entry * has a font set, we will measure it as we come to it, and then we * decide which set to give the geometry routines. */ menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); Tk_GetFontMetrics(menuFont, &menuMetrics); for (i = 0; i < menuPtr->numEntries; i++) { mePtr = menuPtr->entries[i]; mePtr->entryFlags &= ~ENTRY_LAST_COLUMN; if (mePtr->fontPtr != NULL) { tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); Tk_GetFontMetrics(tkfont, &entryMetrics); fmPtr = &entryMetrics; } else { tkfont = menuFont; fmPtr = &menuMetrics; } /* * For every entry, we need to check to see whether or not we * wrap. If we do wrap, then we have to adjust all of the previous * entries' height and y position, because when we see them the * first time, we don't know how big its neighbor might be. */ if ((mePtr->type == SEPARATOR_ENTRY) || (mePtr->type == TEAROFF_ENTRY)) { mePtr->height = mePtr->width = 0; } else { GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width, &height); mePtr->height = height + 2 * activeBorderWidth + 10; mePtr->width = width; GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, fmPtr, &width, &height); mePtr->indicatorSpace = width; if (width > 0) { mePtr->width += width; } mePtr->width += 2 * activeBorderWidth + 10; } if (mePtr->entryFlags & ENTRY_HELP_MENU) { helpMenuIndex = i; } else if (x + mePtr->width + borderWidth > maxWindowWidth) { if (i == lastRowBreak) { mePtr->y = y; mePtr->x = x; lastRowBreak++; y += mePtr->height; currentRowHeight = 0; } else { x = borderWidth; for (j = lastRowBreak; j < i; j++) { menuPtr->entries[j]->y = y + currentRowHeight - menuPtr->entries[j]->height; menuPtr->entries[j]->x = x; x += menuPtr->entries[j]->width; } lastRowBreak = i; y += currentRowHeight; currentRowHeight = mePtr->height; } if (x > maxWidth) { maxWidth = x; } x = borderWidth; } else { x += mePtr->width; if (mePtr->height > currentRowHeight) { currentRowHeight = mePtr->height; } } } lastEntry = menuPtr->numEntries - 1; if (helpMenuIndex == lastEntry) { lastEntry--; } if ((lastEntry >= 0) && (x + menuPtr->entries[lastEntry]->width + borderWidth > maxWidth)) { maxWidth = x + menuPtr->entries[lastEntry]->width + borderWidth; } x = borderWidth; for (j = lastRowBreak; j < menuPtr->numEntries; j++) { if (j == helpMenuIndex) { continue; } menuPtr->entries[j]->y = y + currentRowHeight - menuPtr->entries[j]->height; menuPtr->entries[j]->x = x; x += menuPtr->entries[j]->width; } if (helpMenuIndex != -1) { mePtr = menuPtr->entries[helpMenuIndex]; if (x + mePtr->width + borderWidth > maxWindowWidth) { y += currentRowHeight; currentRowHeight = mePtr->height; x = borderWidth; } else if (mePtr->height > currentRowHeight) { currentRowHeight = mePtr->height; } mePtr->x = maxWindowWidth - borderWidth - mePtr->width; mePtr->y = y + currentRowHeight - mePtr->height; } height = y + currentRowHeight + borderWidth; } width = Tk_Width(menuPtr->tkwin); /* * The X server doesn't like zero dimensions, so round up to at least 1 (a * zero-sized menu should never really occur, anyway). */ if (width <= 0) { width = 1; } if (height <= 0) { height = 1; } menuPtr->totalWidth = maxWidth; menuPtr->totalHeight = height; } /* *---------------------------------------------------------------------- * * DrawTearoffEntry -- * * This procedure draws the background part of a menu. * * Results: * None. * * Side effects: * Commands are output to X to display the menu in its current mode. * *---------------------------------------------------------------------- */ static void DrawTearoffEntry( TkMenu *menuPtr, /* The menu we are drawing */ TCL_UNUSED(TkMenuEntry *), /* The entry we are drawing */ Drawable d, /* The drawable we are drawing into */ TCL_UNUSED(GC), /* The gc we are drawing with */ TCL_UNUSED(Tk_Font), /* The font we are drawing with */ TCL_UNUSED(const Tk_FontMetrics *), /* The metrics we are drawing with */ int x, int y, int width, int height) { XPoint points[2]; int segmentWidth, maxX; Tk_3DBorder border; if (menuPtr->menuType != MAIN_MENU) { return; } points[0].x = x; points[0].y = y + height/2; points[1].y = points[0].y; segmentWidth = 6; maxX = x + width - 1; border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); while (points[0].x < maxX) { points[1].x = points[0].x + segmentWidth; if (points[1].x > maxX) { points[1].x = maxX; } Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1, TK_RELIEF_RAISED); points[0].x += 2 * segmentWidth; } } /* *-------------------------------------------------------------- * * TkpInitializeMenuBindings -- * * For every interp, initializes the bindings for Windows menus. Does * nothing on Mac or XWindows. * * Results: * None. * * Side effects: * C-level bindings are setup for the interp which will handle Alt-key * sequences for menus without beeping or interfering with user-defined * Alt-key bindings. * *-------------------------------------------------------------- */ void TkpInitializeMenuBindings( TCL_UNUSED(Tcl_Interp *), /* The interpreter to set. */ TCL_UNUSED(Tk_BindingTable)) /* The table to add to. */ { /* * Nothing to do. */ } /* *---------------------------------------------------------------------- * * SetHelpMenu -- * * Given a menu, check to see whether or not it is a help menu cascade in * a menubar. If it is, the entry that points to this menu will be * marked. * * RESULTS: * None. * * Side effects: * Will set the ENTRY_HELP_MENU flag appropriately. * *---------------------------------------------------------------------- */ static void SetHelpMenu( TkMenu *menuPtr) /* The menu we are checking */ { TkMenuEntry *cascadeEntryPtr; int useMotifHelp = 0; const char *option = NULL; if (menuPtr->tkwin) { option = Tk_GetOption(menuPtr->tkwin, "useMotifHelp", "UseMotifHelp"); if (option != NULL) { Tcl_GetBoolean(NULL, option, &useMotifHelp); } } if (!useMotifHelp) { return; } for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; cascadeEntryPtr != NULL; cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { if ((cascadeEntryPtr->menuPtr->menuType == MENUBAR) && (cascadeEntryPtr->menuPtr->mainMenuPtr->tkwin != NULL) && (menuPtr->mainMenuPtr->tkwin != NULL)) { TkMenu *mainMenuPtr = cascadeEntryPtr->menuPtr->mainMenuPtr; char *helpMenuName = (char *)ckalloc(strlen(Tk_PathName( mainMenuPtr->tkwin)) + strlen(".help") + 1); strcpy(helpMenuName, Tk_PathName(mainMenuPtr->tkwin)); strcat(helpMenuName, ".help"); if (strcmp(helpMenuName, Tk_PathName(menuPtr->mainMenuPtr->tkwin)) == 0) { cascadeEntryPtr->entryFlags |= ENTRY_HELP_MENU; } else { cascadeEntryPtr->entryFlags &= ~ENTRY_HELP_MENU; } ckfree(helpMenuName); } } } /* *---------------------------------------------------------------------- * * TkpDrawMenuEntry -- * * Draws the given menu entry at the given coordinates with the given * attributes. * * Results: * None. * * Side effects: * X Server commands are executed to display the menu entry. * *---------------------------------------------------------------------- */ void TkpDrawMenuEntry( TkMenuEntry *mePtr, /* The entry to draw */ Drawable d, /* What to draw into */ Tk_Font tkfont, /* Precalculated font for menu */ const Tk_FontMetrics *menuMetricsPtr, /* Precalculated metrics for menu */ int x, /* X-coordinate of topleft of entry */ int y, /* Y-coordinate of topleft of entry */ int width, /* Width of the entry rectangle */ int height, /* Height of the current rectangle */ int strictMotif, /* Boolean flag */ int drawArrow) /* Whether or not to draw the cascade arrow * for cascade items. */ { GC gc, indicatorGC; XColor *indicatorColor, *disableColor = NULL; TkMenu *menuPtr = mePtr->menuPtr; Tk_3DBorder bgBorder, activeBorder; const Tk_FontMetrics *fmPtr; Tk_FontMetrics entryMetrics; int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0; int adjustedY = y + padY; int adjustedHeight = height - 2 * padY; /* * Choose the gc for drawing the foreground part of the entry. */ if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) { gc = mePtr->activeGC; if (gc == NULL) { gc = menuPtr->activeGC; } } else { TkMenuEntry *cascadeEntryPtr; int parentDisabled = 0; for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; cascadeEntryPtr != NULL; cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { if (cascadeEntryPtr->namePtr != NULL) { const char *name = Tcl_GetString(cascadeEntryPtr->namePtr); if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) { if (cascadeEntryPtr->state == ENTRY_DISABLED) { parentDisabled = 1; } break; } } } if (((parentDisabled || (mePtr->state == ENTRY_DISABLED))) && (menuPtr->disabledFgPtr != NULL)) { gc = mePtr->disabledGC; if (gc == NULL) { gc = menuPtr->disabledGC; } } else { gc = mePtr->textGC; if (gc == NULL) { gc = menuPtr->textGC; } } } indicatorGC = mePtr->indicatorGC; if (indicatorGC == NULL) { indicatorGC = menuPtr->indicatorGC; } if (mePtr->indicatorFgPtr) { indicatorColor = Tk_GetColorFromObj(menuPtr->tkwin, mePtr->indicatorFgPtr); } else { indicatorColor = Tk_GetColorFromObj(menuPtr->tkwin, menuPtr->indicatorFgPtr); } if (menuPtr->disabledFgPtr != NULL) { disableColor = Tk_GetColorFromObj(menuPtr->tkwin, menuPtr->disabledFgPtr); } bgBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, (mePtr->borderPtr == NULL) ? menuPtr->borderPtr : mePtr->borderPtr); if (strictMotif) { activeBorder = bgBorder; } else { activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, (mePtr->activeBorderPtr == NULL) ? menuPtr->activeBorderPtr : mePtr->activeBorderPtr); } if (mePtr->fontPtr == NULL) { fmPtr = menuMetricsPtr; } else { tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); Tk_GetFontMetrics(tkfont, &entryMetrics); fmPtr = &entryMetrics; } /* * Need to draw the entire background, including padding. On Unix, for * menubars, we have to draw the rest of the entry taking into account the * padding. */ DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder, bgBorder, x, y, width, height); if (mePtr->type == SEPARATOR_ENTRY) { DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY, width, adjustedHeight); } else if (mePtr->type == TEAROFF_ENTRY) { DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY, width, adjustedHeight); } else { DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY, width, adjustedHeight); DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr, activeBorder, bgBorder, x, adjustedY, width, adjustedHeight, drawArrow); if (!mePtr->hideMargin) { if (mePtr->state == ENTRY_ACTIVE) { bgBorder = activeBorder; } DrawMenuEntryIndicator(menuPtr, mePtr, d, bgBorder, indicatorColor, disableColor, tkfont, fmPtr, x, adjustedY, width, adjustedHeight); } } } /* *---------------------------------------------------------------------- * * GetMenuLabelGeometry -- * * Figures out the size of the label portion of a menu item. * * Results: * widthPtr and heightPtr are filled in with the correct geometry * information. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void GetMenuLabelGeometry( TkMenuEntry *mePtr, /* The entry we are computing */ Tk_Font tkfont, /* The precalculated font */ const Tk_FontMetrics *fmPtr,/* The precalculated metrics */ int *widthPtr, /* The resulting width of the label portion */ int *heightPtr) /* The resulting height of the label * portion */ { TkMenu *menuPtr = mePtr->menuPtr; int haveImage = 0; if (mePtr->image != NULL) { Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr); haveImage = 1; } else if (mePtr->bitmapPtr != NULL) { Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr); haveImage = 1; } else { *heightPtr = 0; *widthPtr = 0; } if (haveImage && (mePtr->compound == COMPOUND_NONE)) { /* * We don't care about the text in this case. */ } else { /* * Either it is compound or we don't have an image. */ if (mePtr->labelPtr != NULL) { int textWidth; const char *label = Tcl_GetString(mePtr->labelPtr); textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength); if ((mePtr->compound != COMPOUND_NONE) && haveImage) { switch ((enum compound) mePtr->compound) { case COMPOUND_TOP: case COMPOUND_BOTTOM: if (textWidth > *widthPtr) { *widthPtr = textWidth; } /* * Add text and padding. */ *heightPtr += fmPtr->linespace + 2; break; case COMPOUND_LEFT: case COMPOUND_RIGHT: if (fmPtr->linespace > *heightPtr) { *heightPtr = fmPtr->linespace; } /* * Add text and padding. */ *widthPtr += textWidth + 2; break; case COMPOUND_CENTER: if (fmPtr->linespace > *heightPtr) { *heightPtr = fmPtr->linespace; } if (textWidth > *widthPtr) { *widthPtr = textWidth; } break; case COMPOUND_NONE: break; } } else { /* * We don't have an image or we're not compound. */ *heightPtr = fmPtr->linespace; *widthPtr = textWidth; } } else { /* * An empty entry still has this height. */ *heightPtr = fmPtr->linespace; } } *heightPtr += 1; } /* *-------------------------------------------------------------- * * TkpComputeStandardMenuGeometry -- * * This procedure is invoked to recompute the size and layout of a menu * that is not a menubar clone. * * Results: * None. * * Side effects: * Fields of menu entries are changed to reflect their current positions, * and the size of the menu window itself may be changed. * *-------------------------------------------------------------- */ void TkpComputeStandardMenuGeometry( TkMenu *menuPtr) /* Structure describing menu. */ { Tk_Font tkfont, menuFont; Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr; int x, y, height, width, indicatorSpace, labelWidth, accelWidth; int windowWidth, windowHeight, accelSpace; Tcl_Size i, j, lastColumnBreak = 0; TkMenuEntry *mePtr; int borderWidth, activeBorderWidth; if (menuPtr->tkwin == NULL) { return; } Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthObj, &borderWidth); Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, &activeBorderWidth); x = y = borderWidth; indicatorSpace = labelWidth = accelWidth = 0; windowHeight = windowWidth = 0; /* * On the Mac especially, getting font metrics can be quite slow, so we * want to do it intelligently. We are going to precalculate them and pass * them down to all of the measuring and drawing routines. We will measure * the font metrics of the menu once. If an entry does not have its own * font set, then we give the geometry/drawing routines the menu's font * and metrics. If an entry has its own font, we will measure that font * and give all of the geometry/drawing the entry's font and metrics. */ menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); Tk_GetFontMetrics(menuFont, &menuMetrics); accelSpace = Tk_TextWidth(menuFont, "M", 1); for (i = 0; i < menuPtr->numEntries; i++) { mePtr = menuPtr->entries[i]; if (mePtr->fontPtr == NULL) { tkfont = menuFont; fmPtr = &menuMetrics; } else { tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); Tk_GetFontMetrics(tkfont, &entryMetrics); fmPtr = &entryMetrics; } if ((i > 0) && mePtr->columnBreak) { if (accelWidth != 0) { labelWidth += accelSpace; } for (j = lastColumnBreak; j < i; j++) { menuPtr->entries[j]->indicatorSpace = indicatorSpace; menuPtr->entries[j]->labelWidth = labelWidth; menuPtr->entries[j]->width = indicatorSpace + labelWidth + accelWidth + 2 * activeBorderWidth; menuPtr->entries[j]->x = x; menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN; } x += indicatorSpace + labelWidth + accelWidth + 2 * activeBorderWidth; windowWidth = x; indicatorSpace = labelWidth = accelWidth = 0; lastColumnBreak = i; y = borderWidth; } if (mePtr->type == SEPARATOR_ENTRY) { GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont, fmPtr, &width, &height); mePtr->height = height; } else if (mePtr->type == TEAROFF_ENTRY) { GetTearoffEntryGeometry(menuPtr, mePtr, tkfont, fmPtr, &width, &height); mePtr->height = height; labelWidth = width; } else { /* * For each entry, compute the height required by that particular * entry, plus three widths: the width of the label, the width to * allow for an indicator to be displayed to the left of the label * (if any), and the width of the accelerator to be displayed to * the right of the label (if any). These sizes depend, of course, * on the type of the entry. */ GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width, &height); mePtr->height = height; if (!mePtr->hideMargin) { width += MENU_MARGIN_WIDTH; } if (width > labelWidth) { labelWidth = width; } GetMenuAccelGeometry(menuPtr, mePtr, tkfont, fmPtr, &width, &height); if (height > mePtr->height) { mePtr->height = height; } if (!mePtr->hideMargin) { width += MENU_MARGIN_WIDTH; } if (width > accelWidth) { accelWidth = width; } GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, fmPtr, &width, &height); if (height > mePtr->height) { mePtr->height = height; } if (!mePtr->hideMargin) { width += MENU_MARGIN_WIDTH; } if (width > indicatorSpace) { indicatorSpace = width; } mePtr->height += 2 * activeBorderWidth + MENU_DIVIDER_HEIGHT; } mePtr->y = y; y += mePtr->height; if (y > windowHeight) { windowHeight = y; } } if (accelWidth != 0) { labelWidth += accelSpace; } for (j = lastColumnBreak; j < menuPtr->numEntries; j++) { menuPtr->entries[j]->indicatorSpace = indicatorSpace; menuPtr->entries[j]->labelWidth = labelWidth; menuPtr->entries[j]->width = indicatorSpace + labelWidth + accelWidth + 2 * activeBorderWidth; menuPtr->entries[j]->x = x; menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN; } windowWidth = x + indicatorSpace + labelWidth + accelWidth + 2 * activeBorderWidth + borderWidth; windowHeight += borderWidth; /* * The X server doesn't like zero dimensions, so round up to at least 1 (a * zero-sized menu should never really occur, anyway). */ if (windowWidth <= 0) { windowWidth = 1; } if (windowHeight <= 0) { windowHeight = 1; } menuPtr->totalWidth = windowWidth; menuPtr->totalHeight = windowHeight; } /* *---------------------------------------------------------------------- * * TkpMenuNotifyToplevelCreate -- * * This routine reconfigures the menu and the clones indicated by * menuName becuase a toplevel has been created and any system menus need * to be created. Not applicable to UNIX. * * Results: * None. * * Side effects: * An idle handler is set up to do the reconfiguration. * *---------------------------------------------------------------------- */ void TkpMenuNotifyToplevelCreate( TCL_UNUSED(Tcl_Interp *), /* The interp the menu lives in. */ TCL_UNUSED(const char *)) /* The name of the menu to reconfigure. */ { /* * Nothing to do. */ } /* *---------------------------------------------------------------------- * * TkpMenuInit -- * * Does platform-specific initialization of menus. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkpMenuInit(void) { /* * Nothing to do. */ } /* *---------------------------------------------------------------------- * * TkpMenuThreadInit -- * * Does platform-specific initialization of thread-specific menu state. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkpMenuThreadInit(void) { /* * Nothing to do. */ } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */