diff options
Diffstat (limited to 'unix/tkUnixMenu.c')
-rw-r--r-- | unix/tkUnixMenu.c | 1603 |
1 files changed, 1603 insertions, 0 deletions
diff --git a/unix/tkUnixMenu.c b/unix/tkUnixMenu.c new file mode 100644 index 0000000..3179a31 --- /dev/null +++ b/unix/tkUnixMenu.c @@ -0,0 +1,1603 @@ +/* + * tkUnixMenu.c -- + * + * This module implements the UNIX platform-specific features of menus. + * + * 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. + * + * SCCS: @(#) tkUnixMenu.c 1.76 97/11/05 09:08:22 + */ + +#include "tkPort.h" +#include "default.h" +#include "tkInt.h" +#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 + +/* + * Procedures used internally. + */ + +static void SetHelpMenu _ANSI_ARGS_((TkMenu *menuPtr)); +static void DrawMenuEntryAccelerator _ANSI_ARGS_(( + TkMenu *menuPtr, TkMenuEntry *mePtr, + Drawable d, GC gc, Tk_Font tkfont, + CONST Tk_FontMetrics *fmPtr, + Tk_3DBorder activeBorder, int x, int y, + int width, int height, int drawArrow)); +static void DrawMenuEntryBackground _ANSI_ARGS_(( + TkMenu *menuPtr, TkMenuEntry *mePtr, + Drawable d, Tk_3DBorder activeBorder, + Tk_3DBorder bgBorder, int x, int y, + int width, int heigth)); +static void DrawMenuEntryIndicator _ANSI_ARGS_(( + TkMenu *menuPtr, TkMenuEntry *mePtr, + Drawable d, GC gc, GC indicatorGC, + Tk_Font tkfont, + CONST Tk_FontMetrics *fmPtr, int x, int y, + int width, int height)); +static void DrawMenuEntryLabel _ANSI_ARGS_(( + 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 _ANSI_ARGS_((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 _ANSI_ARGS_((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 _ANSI_ARGS_((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 _ANSI_ARGS_((TkMenu *menuPtr, + TkMenuEntry *mePtr, Tk_Font tkfont, + CONST Tk_FontMetrics *fmPtr, int *widthPtr, + int *heightPtr)); +static void GetMenuLabelGeometry _ANSI_ARGS_((TkMenuEntry *mePtr, + Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, + int *widthPtr, int *heightPtr)); +static void GetMenuIndicatorGeometry _ANSI_ARGS_(( + TkMenu *menuPtr, TkMenuEntry *mePtr, + Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, + int *widthPtr, int *heightPtr)); +static void GetMenuSeparatorGeometry _ANSI_ARGS_(( + TkMenu *menuPtr, TkMenuEntry *mePtr, + Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, + int *widthPtr, int *heightPtr)); +static void GetTearoffEntryGeometry _ANSI_ARGS_((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(menuPtr) + 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(menuPtr) + TkMenu *menuPtr; +{ + /* + * 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(mEntryPtr) + TkMenuEntry *mEntryPtr; +{ + /* + * 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 + * interp->result contains an error message. + * + * Side effects: + * Configuration information get set for mePtr; old resources + * get freed, if any need it. + * + *---------------------------------------------------------------------- + */ + +int +TkpConfigureMenuEntry(mePtr) + register 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->name != NULL)) { + TkMenuReferences *menuRefPtr; + + menuRefPtr = TkFindMenuReferences(mePtr->menuPtr->interp, + mePtr->name); + 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(mePtr) + TkMenuEntry *mePtr; +{ + 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(tkwin, menuPtr) + 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 +TkpSetMainMenubar(interp, tkwin, menuName) + Tcl_Interp *interp; + Tk_Window tkwin; + char *menuName; +{ + /* + * 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(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr) + TkMenu *menuPtr; /* The menu we are drawing. */ + TkMenuEntry *mePtr; /* The entry we are interested in. */ + Tk_Font tkfont; /* The precalculated font */ + CONST Tk_FontMetrics *fmPtr; /* The precalculated metrics */ + int *widthPtr; /* The resulting width */ + int *heightPtr; /* The resulting height */ +{ + if (!mePtr->hideMargin && mePtr->indicatorOn && + ((mePtr->type == CHECK_BUTTON_ENTRY) + || (mePtr->type == RADIO_BUTTON_ENTRY))) { + if ((mePtr->image != NULL) || (mePtr->bitmap != None)) { + *widthPtr = (14 * mePtr->height) / 10; + *heightPtr = mePtr->height; + if (mePtr->type == CHECK_BUTTON_ENTRY) { + mePtr->platformEntryData = + (TkMenuPlatformEntryData) ((65 * mePtr->height) / 100); + } else { + mePtr->platformEntryData = + (TkMenuPlatformEntryData) ((75 * mePtr->height) / 100); + } + } else { + *widthPtr = *heightPtr = mePtr->height; + if (mePtr->type == CHECK_BUTTON_ENTRY) { + mePtr->platformEntryData = (TkMenuPlatformEntryData) + ((80 * mePtr->height) / 100); + } else { + mePtr->platformEntryData = (TkMenuPlatformEntryData) + mePtr->height; + } + } + } else { + *heightPtr = 0; + *widthPtr = menuPtr->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(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr) + 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 */ +{ + *heightPtr = fmPtr->linespace; + if (mePtr->type == CASCADE_ENTRY) { + *widthPtr = 2 * CASCADE_ARROW_WIDTH; + } else if ((menuPtr->menuType != MENUBAR) && (mePtr->accel != NULL)) { + *widthPtr = Tk_TextWidth(tkfont, mePtr->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(menuPtr, mePtr, d, activeBorder, bgBorder, x, y, + width, height) + 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 == tkActiveUid) { + int relief; + bgBorder = activeBorder; + + if ((menuPtr->menuType == MENUBAR) + && ((menuPtr->postedCascade == NULL) + || (menuPtr->postedCascade != mePtr))) { + relief = TK_RELIEF_FLAT; + } else { + relief = TK_RELIEF_RAISED; + } + + Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height, + menuPtr->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(menuPtr, mePtr, d, gc, tkfont, fmPtr, activeBorder, + x, y, width, height, drawArrow) + 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 */ + 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]; + + /* + * Draw accelerator or cascade arrow. + */ + + if (menuPtr->menuType == MENUBAR) { + return; + } + + if ((mePtr->type == CASCADE_ENTRY) && drawArrow) { + points[0].x = x + width - menuPtr->borderWidth + - menuPtr->activeBorderWidth - CASCADE_ARROW_WIDTH; + points[0].y = y + (height - CASCADE_ARROW_HEIGHT)/2; + points[1].x = points[0].x; + points[1].y = points[0].y + CASCADE_ARROW_HEIGHT; + points[2].x = points[0].x + CASCADE_ARROW_WIDTH; + points[2].y = points[0].y + CASCADE_ARROW_HEIGHT/2; + Tk_Fill3DPolygon(menuPtr->tkwin, d, activeBorder, points, 3, + DECORATION_BORDER_WIDTH, + (menuPtr->postedCascade == mePtr) + ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED); + } else if (mePtr->accel != NULL) { + int left = x + mePtr->labelWidth + menuPtr->activeBorderWidth + + mePtr->indicatorSpace; + if (menuPtr->menuType == MENUBAR) { + left += 5; + } + Tk_DrawChars(menuPtr->display, d, gc, tkfont, mePtr->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(menuPtr, mePtr, d, gc, indicatorGC, tkfont, fmPtr, + x, y, width, height) + TkMenu *menuPtr; /* The menu we are drawing */ + TkMenuEntry *mePtr; /* The entry we are drawing */ + Drawable d; /* The drawable to draw into */ + GC gc; /* The gc to draw with */ + GC indicatorGC; /* The gc that indicators draw with */ + Tk_Font tkfont; /* The font to draw with */ + CONST Tk_FontMetrics *fmPtr; /* The font metrics of the font */ + int x; /* The left of the entry rect */ + int y; /* The top of the entry rect */ + int width; /* Width of menu entry */ + int height; /* Height of menu entry */ +{ + + /* + * Draw check-button indicator. + */ + + if ((mePtr->type == CHECK_BUTTON_ENTRY) + && mePtr->indicatorOn) { + int dim, top, left; + + dim = (int) mePtr->platformEntryData; + left = x + menuPtr->activeBorderWidth + + (mePtr->indicatorSpace - dim)/2; + if (menuPtr->menuType == MENUBAR) { + left += 5; + } + top = y + (height - dim)/2; + Tk_Fill3DRectangle(menuPtr->tkwin, d, menuPtr->border, left, top, dim, + dim, DECORATION_BORDER_WIDTH, TK_RELIEF_SUNKEN); + left += DECORATION_BORDER_WIDTH; + top += DECORATION_BORDER_WIDTH; + dim -= 2*DECORATION_BORDER_WIDTH; + if ((dim > 0) && (mePtr->entryFlags + & ENTRY_SELECTED)) { + XFillRectangle(menuPtr->display, d, indicatorGC, left, top, + (unsigned int) dim, (unsigned int) dim); + } + } + + /* + * Draw radio-button indicator. + */ + + if ((mePtr->type == RADIO_BUTTON_ENTRY) + && mePtr->indicatorOn) { + XPoint points[4]; + int radius; + + radius = ((int) mePtr->platformEntryData)/2; + points[0].x = x + (mePtr->indicatorSpace + - (int) mePtr->platformEntryData)/2; + points[0].y = y + (height)/2; + 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 (mePtr->entryFlags & ENTRY_SELECTED) { + XFillPolygon(menuPtr->display, d, indicatorGC, points, 4, Convex, + CoordModeOrigin); + } else { + Tk_Fill3DPolygon(menuPtr->tkwin, d, menuPtr->border, points, 4, + DECORATION_BORDER_WIDTH, TK_RELIEF_FLAT); + } + Tk_Draw3DPolygon(menuPtr->tkwin, d, menuPtr->border, points, 4, + DECORATION_BORDER_WIDTH, TK_RELIEF_SUNKEN); + } +} + +/* + *---------------------------------------------------------------------- + * + * 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(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height) + TkMenu *menuPtr; /* The menu we are drawing */ + TkMenuEntry *mePtr; /* The entry we are drawing */ + Drawable d; /* The drawable we are using */ + GC gc; /* The gc to draw into */ + Tk_Font tkfont; /* The font to draw with */ + CONST Tk_FontMetrics *fmPtr; /* The font metrics from the font */ + int x; + int y; + int width; + int height; +{ + XPoint points[2]; + int margin; + + if (menuPtr->menuType == MENUBAR) { + return; + } + + margin = (fmPtr->ascent + fmPtr->descent)/2; + points[0].x = x; + points[0].y = y + height/2; + points[1].x = width - 1; + points[1].y = points[0].y; + Tk_Draw3DPolygon(menuPtr->tkwin, d, menuPtr->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( + menuPtr, /* The menu we are drawing */ + mePtr, /* The entry we are drawing */ + d, /* What we are drawing into */ + gc, /* The gc we are drawing into */ + tkfont, /* The precalculated font */ + fmPtr, /* The precalculated font metrics */ + x, /* left edge */ + y, /* right edge */ + width, /* width of entry */ + height) /* height of entry */ + TkMenu *menuPtr; + TkMenuEntry *mePtr; + Drawable d; + GC gc; + Tk_Font tkfont; + CONST Tk_FontMetrics *fmPtr; + int x, y, width, height; +{ + int baseline; + int indicatorSpace = mePtr->indicatorSpace; + int leftEdge = x + indicatorSpace + menuPtr->activeBorderWidth; + int imageHeight, imageWidth; + + if (menuPtr->menuType == MENUBAR) { + leftEdge += 5; + } + + /* + * Draw label or bitmap or image for entry. + */ + + baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2; + if (mePtr->image != NULL) { + Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight); + if ((mePtr->selectImage != NULL) + && (mePtr->entryFlags & ENTRY_SELECTED)) { + Tk_RedrawImage(mePtr->selectImage, 0, 0, + imageWidth, imageHeight, d, leftEdge, + (int) (y + (mePtr->height - imageHeight)/2)); + } else { + Tk_RedrawImage(mePtr->image, 0, 0, imageWidth, + imageHeight, d, leftEdge, + (int) (y + (mePtr->height - imageHeight)/2)); + } + } else if (mePtr->bitmap != None) { + int width, height; + + Tk_SizeOfBitmap(menuPtr->display, + mePtr->bitmap, &width, &height); + XCopyPlane(menuPtr->display, + mePtr->bitmap, d, + gc, 0, 0, (unsigned) width, (unsigned) height, leftEdge, + (int) (y + (mePtr->height - height)/2), 1); + } else { + if (mePtr->labelLength > 0) { + Tk_DrawChars(menuPtr->display, d, gc, + tkfont, mePtr->label, mePtr->labelLength, + leftEdge, baseline); + DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, + width, height); + } + } + + if (mePtr->state == tkDisabledUid) { + if (menuPtr->disabledFg == NULL) { + XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y, + (unsigned) width, (unsigned) height); + } else if ((mePtr->image != NULL) + && (menuPtr->disabledImageGC != None)) { + XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC, + leftEdge, + (int) (y + (mePtr->height - imageHeight)/2), + (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(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height) + 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; + int width; + int height; +{ + int indicatorSpace = mePtr->indicatorSpace; + if (mePtr->underline >= 0) { + int leftEdge = x + indicatorSpace + menuPtr->activeBorderWidth; + if (menuPtr->menuType == MENUBAR) { + leftEdge += 5; + } + + Tk_UnderlineChars(menuPtr->display, d, gc, tkfont, mePtr->label, + leftEdge, y + (height + fmPtr->ascent - fmPtr->descent) / 2, + mePtr->underline, mePtr->underline + 1); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpPostMenu -- + * + * Posts a menu on the screen + * + * Results: + * None. + * + * Side effects: + * The menu is posted and handled. + * + *---------------------------------------------------------------------- + */ + +int +TkpPostMenu(interp, menuPtr, x, y) + Tcl_Interp *interp; + TkMenu *menuPtr; + int x; + int y; +{ + return TkPostTearoffMenu(interp, menuPtr, x, y); +} + +/* + *---------------------------------------------------------------------- + * + * 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(menuPtr, mePtr, tkfont, fmPtr, widthPtr, + heightPtr) + TkMenu *menuPtr; /* The menu we are measuring */ + TkMenuEntry *mePtr; /* The entry we are measuring */ + Tk_Font tkfont; /* 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(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr) + TkMenu *menuPtr; /* The menu we are drawing */ + TkMenuEntry *mePtr; /* 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 != MASTER_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(menuPtr) + TkMenu *menuPtr; /* Structure describing menu. */ +{ + Tk_Font tkfont; + Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr; + int width, height; + int i, j; + int x, y, currentRowHeight, currentRowWidth, maxWidth; + int maxWindowWidth; + int lastRowBreak; + int helpMenuIndex = -1; + TkMenuEntry *mePtr; + int lastEntry; + + if (menuPtr->tkwin == NULL) { + return; + } + + maxWidth = 0; + if (menuPtr->numEntries == 0) { + height = 0; + } else { + maxWindowWidth = Tk_Width(menuPtr->tkwin); + if (maxWindowWidth == 1) { + maxWindowWidth = 0x7ffffff; + } + currentRowHeight = 0; + x = y = menuPtr->borderWidth; + lastRowBreak = 0; + currentRowWidth = 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 measureing 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. + */ + + Tk_GetFontMetrics(menuPtr->tkfont, &menuMetrics); + + for (i = 0; i < menuPtr->numEntries; i++) { + mePtr = menuPtr->entries[i]; + mePtr->entryFlags &= ~ENTRY_LAST_COLUMN; + tkfont = mePtr->tkfont; + if (tkfont == NULL) { + tkfont = menuPtr->tkfont; + fmPtr = &menuMetrics; + } else { + Tk_GetFontMetrics(tkfont, &entryMetrics); + fmPtr = &entryMetrics; + } + + /* + * 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 * menuPtr->activeBorderWidth + 10; + mePtr->width = width; + + GetMenuIndicatorGeometry(menuPtr, mePtr, + tkfont, fmPtr, &width, &height); + mePtr->indicatorSpace = width; + if (width > 0) { + mePtr->width += width; + } + mePtr->width += 2 * menuPtr->activeBorderWidth + 10; + } + if (mePtr->entryFlags & ENTRY_HELP_MENU) { + helpMenuIndex = i; + } else if (x + mePtr->width + menuPtr->borderWidth + > maxWindowWidth) { + + if (i == lastRowBreak) { + mePtr->y = y; + mePtr->x = x; + lastRowBreak++; + y += mePtr->height; + currentRowHeight = 0; + } else { + x = menuPtr->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 = menuPtr->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 + + menuPtr->borderWidth > maxWidth)) { + maxWidth = x + menuPtr->entries[lastEntry]->width + + menuPtr->borderWidth; + } + x = menuPtr->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 + menuPtr->borderWidth > maxWindowWidth) { + y += currentRowHeight; + currentRowHeight = mePtr->height; + x = menuPtr->borderWidth; + } else if (mePtr->height > currentRowHeight) { + currentRowHeight = mePtr->height; + } + mePtr->x = maxWindowWidth - menuPtr->borderWidth - mePtr->width; + mePtr->y = y + currentRowHeight - mePtr->height; + } + height = y + currentRowHeight + menuPtr->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(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height) + 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 gc we are drawing with */ + Tk_Font tkfont; /* The font we are drawing with */ + CONST Tk_FontMetrics *fmPtr; /* The metrics we are drawing with */ + int x; + int y; + int width; + int height; +{ + XPoint points[2]; + int margin, segmentWidth, maxX; + + if (menuPtr->menuType != MASTER_MENU) { + return; + } + + margin = (fmPtr->ascent + fmPtr->descent)/2; + points[0].x = x; + points[0].y = y + height/2; + points[1].y = points[0].y; + segmentWidth = 6; + maxX = width - 1; + + 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, menuPtr->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(interp, bindingTable) + Tcl_Interp *interp; /* The interpreter to set. */ + Tk_BindingTable 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(menuPtr) + TkMenu *menuPtr; /* The menu we are checking */ +{ + TkMenuEntry *cascadeEntryPtr; + + for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; + cascadeEntryPtr != NULL; + cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { + if ((cascadeEntryPtr->menuPtr->menuType == MENUBAR) + && (cascadeEntryPtr->menuPtr->masterMenuPtr->tkwin != NULL) + && (menuPtr->masterMenuPtr->tkwin != NULL)) { + TkMenu *masterMenuPtr = cascadeEntryPtr->menuPtr->masterMenuPtr; + char *helpMenuName = ckalloc(strlen(Tk_PathName( + masterMenuPtr->tkwin)) + strlen(".help") + 1); + + strcpy(helpMenuName, Tk_PathName(masterMenuPtr->tkwin)); + strcat(helpMenuName, ".help"); + if (strcmp(helpMenuName, + Tk_PathName(menuPtr->masterMenuPtr->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(mePtr, d, tkfont, menuMetricsPtr, x, y, width, height, + strictMotif, drawArrow) + 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. Only applies + * to Windows. */ +{ + GC gc, indicatorGC; + 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 == tkActiveUid) + && !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 (strcmp(cascadeEntryPtr->name, + Tk_PathName(menuPtr->tkwin)) == 0) { + if (cascadeEntryPtr->state == tkDisabledUid) { + parentDisabled = 1; + } + break; + } + } + + if (((parentDisabled || (mePtr->state == tkDisabledUid))) + && (menuPtr->disabledFg != 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; + } + + bgBorder = mePtr->border; + if (bgBorder == NULL) { + bgBorder = menuPtr->border; + } + if (strictMotif) { + activeBorder = bgBorder; + } else { + activeBorder = mePtr->activeBorder; + if (activeBorder == NULL) { + activeBorder = menuPtr->activeBorder; + } + } + + if (mePtr->tkfont == NULL) { + fmPtr = menuMetricsPtr; + } else { + tkfont = mePtr->tkfont; + 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, x, adjustedY, width, adjustedHeight, drawArrow); + if (!mePtr->hideMargin) { + DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, 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(mePtr, tkfont, fmPtr, widthPtr, heightPtr) + 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; + + if (mePtr->image != NULL) { + Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr); + } else if (mePtr->bitmap != (Pixmap) NULL) { + Tk_SizeOfBitmap(menuPtr->display, mePtr->bitmap, widthPtr, heightPtr); + } else { + *heightPtr = fmPtr->linespace; + + if (mePtr->label != NULL) { + *widthPtr = Tk_TextWidth(tkfont, mePtr->label, mePtr->labelLength); + } else { + *widthPtr = 0; + } + } + *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( + menuPtr) /* Structure describing menu. */ + TkMenu *menuPtr; +{ + Tk_Font tkfont; + Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr; + int x, y, height, width, indicatorSpace, labelWidth, accelWidth; + int windowWidth, windowHeight, accelSpace; + int i, j, lastColumnBreak = 0; + TkMenuEntry *mePtr; + + if (menuPtr->tkwin == NULL) { + return; + } + + x = y = menuPtr->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. + */ + + Tk_GetFontMetrics(menuPtr->tkfont, &menuMetrics); + accelSpace = Tk_TextWidth(menuPtr->tkfont, "M", 1); + + for (i = 0; i < menuPtr->numEntries; i++) { + mePtr = menuPtr->entries[i]; + tkfont = mePtr->tkfont; + if (tkfont == NULL) { + tkfont = menuPtr->tkfont; + fmPtr = &menuMetrics; + } else { + 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 * menuPtr->activeBorderWidth; + menuPtr->entries[j]->x = x; + menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN; + } + x += indicatorSpace + labelWidth + accelWidth + + 2 * menuPtr->activeBorderWidth; + windowWidth = x; + indicatorSpace = labelWidth = accelWidth = 0; + lastColumnBreak = i; + y = menuPtr->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 * menuPtr->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 * menuPtr->activeBorderWidth; + menuPtr->entries[j]->x = x; + menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN; + } + windowWidth = x + indicatorSpace + labelWidth + accelWidth + + 2 * menuPtr->activeBorderWidth + 2 * menuPtr->borderWidth; + + + windowHeight += menuPtr->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(interp, menuName) + Tcl_Interp *interp; /* The interp the menu lives in. */ + char *menuName; /* The name of the menu to + * reconfigure. */ +{ + /* + * Nothing to do. + */ +} + +/* + *---------------------------------------------------------------------- + * + * TkpMenuInit -- + * + * Does platform-specific initialization of menus. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpMenuInit() +{ + /* + * Nothing to do. + */ +} |