diff options
Diffstat (limited to 'carbon/tkMacOSXMenu.c')
-rw-r--r-- | carbon/tkMacOSXMenu.c | 4745 |
1 files changed, 4745 insertions, 0 deletions
diff --git a/carbon/tkMacOSXMenu.c b/carbon/tkMacOSXMenu.c new file mode 100644 index 0000000..a31daf8 --- /dev/null +++ b/carbon/tkMacOSXMenu.c @@ -0,0 +1,4745 @@ +/* + * tkMacOSXMenu.c -- + * + * This module implements the Mac-platform specific features of menus. + * + * Copyright (c) 1996-1997 by Sun Microsystems, Inc. + * Copyright 2001, Apple Computer, Inc. + * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net> + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tkMacOSXMenu.c,v 1.2 2010/05/10 20:58:18 nijtmans Exp $ + */ + +#include "tkMacOSXPrivate.h" +#include "tkMenubutton.h" +#include "tkMenu.h" +#include "tkColor.h" +#include "tkFont.h" +#include "tkMacOSXDebug.h" + +/* +#ifdef TK_MAC_DEBUG +#define TK_MAC_DEBUG_MENUS +#endif +*/ + +#define USE_TK_MDEF + +typedef struct MacMenu { + MenuRef menuHdl; /* The Menu Manager data structure. */ +#ifdef USE_TK_MDEF + int useMDEF; /* true if this menu uses the MDEF */ +#endif +} MacMenu; + +typedef struct MenuEntryUserData { + Drawable mdefDrawable; + TkMenuEntry *mePtr; + Tk_Font tkfont; + Tk_FontMetrics *fmPtr; +} MenuEntryUserData; + +/* + * Platform specific flags for menu entries + * + * ENTRY_COMMAND_ACCEL Indicates the entry has the command key + * in its accelerator string. + * ENTRY_OPTION_ACCEL Indicates the entry has the option key + * in its accelerator string. + * ENTRY_SHIFT_ACCEL Indicates the entry has the shift key + * in its accelerator string. + * ENTRY_CONTROL_ACCEL Indicates the entry has the control key + * in its accelerator string. + */ + +#define ENTRY_COMMAND_ACCEL ENTRY_PLATFORM_FLAG1 +#define ENTRY_OPTION_ACCEL ENTRY_PLATFORM_FLAG2 +#define ENTRY_SHIFT_ACCEL ENTRY_PLATFORM_FLAG3 +#define ENTRY_CONTROL_ACCEL ENTRY_PLATFORM_FLAG4 +#define ENTRY_ACCEL_MASK (ENTRY_COMMAND_ACCEL | ENTRY_OPTION_ACCEL \ + | ENTRY_SHIFT_ACCEL | ENTRY_CONTROL_ACCEL) +#define MODIFIER_NUM 4 + +/* + * This structure is used to keep track of subfields within Macintosh menu + * items. + */ + +typedef struct EntryGeometry { + int accelTextStart; /* Offset into the accel string where the text + * starts. Everything before this is modifier + * key descriptions. */ + int modifierWidth; /* Width of modifier symbols. */ + int accelTextWidth; /* Width of the text after the modifier + * keys. */ + int nonAccelMargin; /* The width of the margin for entries without + * accelerators. */ + int modifierNum; /* Number of modifiers */ + Tcl_UniChar modifierUniChars[MODIFIER_NUM]; + /* Modifiers in unicode */ + char accelGlyph; /* Accelerator glyph, if any */ +} EntryGeometry; + +/* + * Structure to keep track of toplevel windows and their menubars. + */ + +typedef struct TopLevelMenubarList { + struct TopLevelMenubarList *nextPtr; + /* The next window in the list. */ + Tk_Window tkwin; /* The toplevel window. */ + TkMenu *menuPtr; /* The menu associated with this toplevel. */ +} TopLevelMenubarList; + +/* + * Platform-specific flags for menus. + * + * MENU_APPLE_MENU 0 indicates a custom Apple menu has not been + * installed; 1 a custom Apple menu has been + * installed. + * MENU_HELP_MENU 0 indicates a custom Help menu has not been + * installed; 1 a custom Help menu has been + * installed. + * MENU_RECONFIGURE_PENDING 1 indicates that an idle handler has been + * scheduled to reconfigure the Macintosh + * MenuHandle. + */ + +#define MENU_APPLE_MENU MENU_PLATFORM_FLAG1 +#define MENU_HELP_MENU MENU_PLATFORM_FLAG2 +#define MENU_RECONFIGURE_PENDING MENU_PLATFORM_FLAG3 + +#define CASCADE_CMD (0x1b) /* The special command char for cascade + * menus. */ +#define MENUBAR_REDRAW_PENDING 1 + +static int gNoTkMenus = 0; /* This is used by Tk_MacOSXTurnOffMenus as + * the flag that Tk is not to draw any + * menus. */ + +static Tcl_HashTable commandTable; + /* The list of menuInstancePtrs associated + * with menu ids. */ +static short currentAppleMenuID; + /* The id of the current Apple menu. 0 for + * none. */ +static short currentHelpMenuID; /* The id of the current Help menu. 0 for + * none. */ +static Tcl_Interp *currentMenuBarInterp; + /* The interpreter of the window that owns the + * current menubar. */ +static char *currentMenuBarName; + /* Malloced. Name of current menu in menu bar. + * NULL if no menu set. TO DO: make this a + * DString. */ +static Tk_Window currentMenuBarOwner; + /* Which window owns the current menu bar. */ +static int inPostMenu; /* We cannot be re-entrant like X windows. */ +static short lastMenuID; /* To pass to NewMenu; need to figure out a + * good way to do this. */ +static short lastCascadeID; /* Cascades have to have ids that are less + * than 256. */ +static int menuBarFlags; /* Used for whether the menu bar needs + * redrawing or not. */ + +struct MenuCommandHandlerData { /* This is the ClientData we pass to */ + TkMenu *menuPtr; /* Tcl_DoWhenIdle to move handling */ + int index; /* menu commands to the event loop. */ +}; + +static TopLevelMenubarList *windowListPtr; + /* A list of windows that have menubars set. */ + +/* + * Array of unicode, charcode and utf representations of the most common + * special menu symbols. + */ + +typedef struct MenuSymbol { + const Tcl_UniChar unicode; + const char charCode; + /* char padding; */ + int utfLen, width; + char utf[TCL_UTF_MAX + 1]; +} MenuSymbol; + +static MenuSymbol menuSymbols[] = { + {kCommandUnicode, kCommandCharCode}, + {kOptionUnicode, kMenuOptionGlyph}, + {kControlUnicode, kMenuControlGlyph}, + {kShiftUnicode, kMenuShiftGlyph}, + {kCheckUnicode, kCheckCharCode}, + {kDiamondUnicode, kDiamondCharCode}, + {kBulletUnicode, kBulletCharCode}, + {0x2026, kNullCharCode}, + {0, 0}, +}; + +enum MenuSymbolIdx { + COMMAND_SYMBOL, + OPTION_SYMBOL, + CONTROL_SYMBOL, + SHIFT_SYMBOL, + CHECK_SYMBOL, + DIAMDOND_SYMBOL, + BULLET_SYMBOL, + ELLIPSIS_SYMBOL, +}; + +MenuRef tkCurrentAppleMenu = NULL; + +static SInt32 menuMarkColumnWidth = 0, menuMarkIndent = 0; +static SInt32 menuTextLeadingEdgeMargin = 0, menuTextTrailingEdgeMargin = 0; +static SInt16 menuItemExtraHeight = 0, menuItemExtraWidth = 0; +static SInt16 menuSeparatorHeight = 0; + +/* + * Forward declarations for procedures defined later in this file: + */ + +MODULE_SCOPE int TkMacOSXGetNewMenuID(Tcl_Interp *interp, + TkMenu *menuInstPtr, int cascade, + short *menuIDPtr); +MODULE_SCOPE void TkMacOSXFreeMenuID(short menuID); + +static void CompleteIdlers(TkMenu *menuPtr); +static void DrawMenuBarWhenIdle(ClientData clientData); +static void DrawMenuEntryAccelerator(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(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, GC gc, + GC indicatorGC, 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 EventuallyInvokeMenu(ClientData data); +static void GetEntryText(TkMenuEntry *mePtr, + Tcl_DString *dStringPtr); +static void GetMenuAccelGeometry(TkMenu *menuPtr, + TkMenuEntry *mePtr, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, int *modWidthPtr, + int *textWidthPtr, 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 TkMenuEntry * GetParentMenuEntry(TkMenu *menuPtr); +static void GetTearoffEntryGeometry(TkMenu *menuPtr, + TkMenuEntry *mePtr, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, int *widthPtr, + int *heightPtr); +static char FindMarkCharacter(TkMenuEntry *mePtr); +static int GetUtfMarkCharacter(char markChar, + const char **markUtfPtr); +static TkMenu * MenuPtrForMenuRef(MenuRef menu); +static int ParseAccelerators(const char **accelStringPtr, + int *modifierNumPtr, + Tcl_UniChar *modifierUniChars, + int *modifierWidth); +static void MenuSelectEvent(TkMenu *menuPtr); +static void ReconfigureIndividualMenu(TkMenu *menuPtr, + MenuHandle macMenuHdl, int base); +static void ReconfigureMacintoshMenu(ClientData clientData); +static void RecursivelyClearActiveMenu(TkMenu *menuPtr); +static void RecursivelyDeleteMenu(TkMenu *menuPtr); +static void RecursivelyInsertMenu(TkMenu *menuPtr); +static void SetDefaultMenubar(void); +static int SetMenuCascade(TkMenu *menuPtr); + +#ifdef USE_TK_MDEF +#define SCREEN_MARGIN 5 +static MacDrawable macMDEFDrawable; + /* Drawable for use by MDEF code */ +static int MDEFScrollFlag = 0; /* Used so that popups don't scroll too + * soon. */ +static MenuItemDrawingUPP tkThemeMenuItemDrawingUPP; + /* Points to the UPP for theme Item + * drawing. */ +static Tcl_Obj *useMDEFVar; + +static void DrawMenuBackground(TkMenu *menuPtr, Rect *menuRectPtr, + Drawable d); +static void MenuDefProc(short message, MenuHandle menu, + Rect *menuRectPtr, Point hitPt, short *whichItem); +static void HandleMenuHiliteMsg(MenuRef menu, Rect *menuRectPtr, + Point hitPt, SInt16 *whichItem, TkMenu *menuPtr); +static void HandleMenuDrawMsg(MenuRef menu, Rect *menuRectPtr, + Point hitPt, SInt16 *whichItem, TkMenu *menuPtr); +static void HandleMenuFindItemMsg(MenuRef menu, Rect *menuRectPtr, + Point hitPt, SInt16 *whichItem, TkMenu *menuPtr); +static void HandleMenuPopUpMsg(MenuRef menu, Rect *menuRectPtr, + Point hitPt, SInt16 *whichItem, TkMenu *menuPtr); +static void HandleMenuCalcItemMsg(MenuRef menu, Rect *menuRectPtr, + Point hitPt, SInt16 *whichItem, TkMenu *menuPtr); +static void AppearanceEntryDrawWrapper(TkMenuEntry *mePtr, + Rect *menuRectPtr, MenuTrackingData *mtdPtr, + Drawable d, Tk_FontMetrics *fmPtr, Tk_Font tkfont, + int erase); +static pascal void ThemeMenuItemDrawingProc(const Rect *inBounds, + SInt16 inDepth, Boolean inIsColorDevice, + SInt32 inUserData); +#else /* USE_TK_MDEF */ +# define useMDEF 0 +#endif /* USE_TK_MDEF */ + +#define IS_THEME_MENU_FONT(tkfont) (strcmp(Tk_NameOfFont(tkfont), "menu") == 0) + +/* + *---------------------------------------------------------------------- + * + * DrawThemeText -- + * + * Wrapper for DrawThemeTextBox API. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +DrawThemeText( + Drawable d, + GC gc, + CFStringRef string, + ThemeFontID font, + ThemeDrawState drawState, + const Rect* bounds, + int baseline, + int just) +{ + TkMacOSXDrawingContext dc; + Rect adjustedBounds; + + /* + * Menu item text drawn with the .Keyboard font (used for + * kThemeMenuItemCmdKeyFont) won't always have the same ascent and + * baseline as text drawn with the regular menu item font, since the + * glyphs in the .Keyboard font may have a different height. Therefore, we + * first determine the baseline of the text and then adjust the bounds + * rect so the baseline aligns with the overall baseline of the menu item. + */ + + if (font == kThemeMenuItemCmdKeyFont) { + Point size; + SInt16 cmdKeyBaseline; + + GetThemeTextDimensions(string, font, drawState, false, &size, + &cmdKeyBaseline); + adjustedBounds = *bounds; + OffsetRect(&adjustedBounds, 0, baseline - bounds->top - size.v - + cmdKeyBaseline); + bounds = &adjustedBounds; + } + if (TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + ChkErr(DrawThemeTextBox, string, font, drawState, false, bounds, just, + dc.context); + TkMacOSXRestoreDrawingContext(&dc); + } +} + +/* + *---------------------------------------------------------------------- + * + * MeasureThemeText -- + * + * Wrapper for GetThemeTextDimensions API. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +MeasureThemeText( + CFStringRef string, + ThemeFontID font) +{ + Point pt; + + ChkErr(GetThemeTextDimensions, string, font, kThemeStateActive, false, &pt, + NULL); + return pt.h; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXUseID -- + * + * Take the ID out of the available list for new menus. Used by the + * default menu bar's menus so that they do not get created at the tk + * level. See TkMacOSXGetNewMenuID for more information. + * + * Results: + * Returns TCL_OK if the id was not in use. Returns TCL_ERROR if the id + * was in use. + * + * Side effects: + * A hash table entry in the command table is created with a NULL value. + * + *---------------------------------------------------------------------- + */ + +int +TkMacOSXUseMenuID( + short macID) /* The id to take out of the table */ +{ + Tcl_HashEntry *commandEntryPtr; + int newEntry; + int iMacID = macID; /* Do this to remove compiler warning */ + + TkMenuInit(); + commandEntryPtr = Tcl_CreateHashEntry(&commandTable, (char *) iMacID, + &newEntry); + if (!newEntry) { + return TCL_ERROR; + } + Tcl_SetHashValue(commandEntryPtr, NULL); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGetNewMenuID -- + * + * Allocates a new menu id and marks it in use. Each menu on the mac must + * be designated by a unique id, which is a short. In addition, some ids + * are reserved by the system. Since Tk uses mostly dynamic menus, we + * must allocate and free these ids on the fly. We use the id as a key + * into a hash table; if there is no hash entry, we know that we can use + * the id. + * + * Carbon allows a much larger number of menus than the old APIs. I + * believe this is 32768, but am not sure. This code just uses 2000 as + * the upper limit. Unfortunately tk leaks menus when cloning, under some + * circumstances (see bug on sourceforge). + * + * Results: + * Returns TCL_OK if succesful; TCL_ERROR if there are no more ids of the + * appropriate type to allocate. menuIDPtr contains the new id if + * succesful. + * + * Side effects: + * An entry is created for the menu in the command hash table, and the + * hash entry is stored in the appropriate field in the menu data + * structure. + * + *---------------------------------------------------------------------- + */ + +int +TkMacOSXGetNewMenuID( + Tcl_Interp *interp, /* Used for error reporting */ + TkMenu *menuPtr, /* The menu we are working with */ + int cascade, /* 0 if we are working with a normal menu; + * 1 if we are working with a cascade */ + short *menuIDPtr) /* The resulting id */ +{ + int found = 0; + int newEntry; + Tcl_HashEntry *commandEntryPtr = NULL; + short returnID = *menuIDPtr; + + /* + * The following code relies on shorts and unsigned chars wrapping when + * the highest value is incremented. Also, the values between 236 and 255 + * inclusive are reserved for DA's by the Mac OS. + */ + + if (!cascade) { + short curID = lastMenuID + 1; + + if (curID == 236) { + curID = 256; + } + + while (curID != lastMenuID) { + int iCurID = curID; + commandEntryPtr = Tcl_CreateHashEntry(&commandTable, + (char *) iCurID, &newEntry); + if (newEntry == 1) { + found = 1; + lastMenuID = returnID = curID; + break; + } + curID++; + if (curID == 236) { + curID = 256; + } + } + } else { + /* + * Cascade ids must be between 0 and 235 only, so they must be dealt + * with separately. + */ + + short curID = lastCascadeID + 1; + + if (curID == 2000) { + curID = 0; + } + + while (curID != lastCascadeID) { + int iCurID = curID; + commandEntryPtr = Tcl_CreateHashEntry(&commandTable, + (char *) iCurID, &newEntry); + if (newEntry == 1) { + found = 1; + lastCascadeID = returnID = curID; + break; + } + curID++; + if (curID == 2000) { + curID = 0; + } + } + } + + if (!found) { + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "No more menus can be allocated.", NULL); + return TCL_ERROR; + } + Tcl_SetHashValue(commandEntryPtr, menuPtr); + *menuIDPtr = returnID; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXFreeMenuID -- + * + * Marks the id as free. + * + * Results: + * None. + * + * Side effects: + * The hash table entry for the ID is cleared. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXFreeMenuID( + short menuID) /* The id to free */ +{ + Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&commandTable, + (char*)(intptr_t)menuID); + + if (entryPtr != NULL) { + Tcl_DeleteHashEntry(entryPtr); + } + if (menuID == currentAppleMenuID) { + currentAppleMenuID = 0; + } + if (menuID == currentHelpMenuID) { + currentHelpMenuID = 0; + } +} + +/* + *---------------------------------------------------------------------- + * + * MenuPtrForMenuRef -- + * + * Returns a pointer to the TkMenu corresponding to a given Carbon + * MenuRef. + * + * Results: + * Returns a pointer to a TkMenu or NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TkMenu * +MenuPtrForMenuRef( + MenuRef menu) +{ + TkMenu *menuPtr = NULL; + MenuID menuID = GetMenuID(menu); + Tcl_HashEntry *commandEntryPtr = + Tcl_FindHashEntry(&commandTable, (char*)(intptr_t)menuID); + + if (commandEntryPtr) { + menuPtr = Tcl_GetHashValue(commandEntryPtr); + } + return menuPtr; +} + +/* + *---------------------------------------------------------------------- + * + * GetParentMenuEntry -- + * + * Returns a pointer to the parent's TkMenuEntry of a given TkMenu. + * + * Results: + * Returns a pointer to a TkMenuEntry or NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TkMenuEntry* +GetParentMenuEntry( + TkMenu *menuPtr) +{ + TkMenuEntry *cascadeEntryPtr; + + for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; + cascadeEntryPtr != NULL; + cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { + const char *name = (cascadeEntryPtr->namePtr == NULL) ? "" + : Tcl_GetString(cascadeEntryPtr->namePtr); + + if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) { + break; + } + } + return cascadeEntryPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TkpNewMenu -- + * + * Gets a new blank menu. Only the platform specific options are filled + * in. + * + * Results: + * Returns a standard TCL error. + * + * Side effects: + * Allocates a Macintosh menu handle and puts in the platformData field + * of the menuPtr. + * + *---------------------------------------------------------------------- + */ + +int +TkpNewMenu( + TkMenu *menuPtr) /* The common structure we are making the + * platform structure for. */ +{ + short menuID; + MenuRef macMenuHdl; +#ifdef USE_TK_MDEF + MenuDefSpec menuDefSpec; + Tcl_Obj *useMDEFObjPtr; + int useMDEF = 1; +#endif + int error = TCL_OK; + OSStatus err; + CFStringRef cfStr; + + error = TkMacOSXGetNewMenuID(menuPtr->interp, menuPtr, 0, &menuID); + if (error != TCL_OK) { + return error; + } + err = ChkErr(CreateNewMenu, menuID, kMenuAttrDoNotUseUserCommandKeys, + &macMenuHdl); + if (err != noErr) { + Tcl_AppendResult(menuPtr->interp, "CreateNewMenu failed.", NULL); + return TCL_ERROR; + } + cfStr = CFStringCreateWithCString(NULL, Tk_PathName(menuPtr->tkwin), + kCFStringEncodingUTF8); + if (!cfStr) { + Tcl_AppendResult(menuPtr->interp, "CFStringCreateWithCString failed.", + NULL); + return TCL_ERROR; + } + err = ChkErr(SetMenuTitleWithCFString, macMenuHdl, cfStr); + CFRelease(cfStr); + if (err != noErr) { + Tcl_AppendResult(menuPtr->interp, "SetMenuTitleWithCFString failed.", + NULL); + return TCL_ERROR; + } + + menuPtr->platformData = ckalloc(sizeof(MacMenu)); + ((MacMenu *) menuPtr->platformData)->menuHdl = macMenuHdl; + +#ifdef USE_TK_MDEF + /* + * Check whether we want to use the custom mdef or not. For now + * the default is to use it unless the variable is explicitly + * set to no. + */ + + useMDEFObjPtr = Tcl_ObjGetVar2(menuPtr->interp, useMDEFVar, NULL, + TCL_GLOBAL_ONLY); + if (useMDEFObjPtr == NULL || Tcl_GetBooleanFromObj(NULL, useMDEFObjPtr, + &useMDEF) == TCL_ERROR || useMDEF) { + menuDefSpec.defType = kMenuDefProcPtr; + menuDefSpec.u.defProc = MenuDefProc; + ChkErr(SetMenuDefinition, macMenuHdl, &menuDefSpec); + } + ((MacMenu *) menuPtr->platformData)->useMDEF = useMDEF; +#endif /* USE_TK_MDEF */ + + if ((currentMenuBarInterp == menuPtr->interp) + && (currentMenuBarName != NULL)) { + Tk_Window parentWin = Tk_Parent(menuPtr->tkwin); + + if (strcmp(currentMenuBarName, Tk_PathName(parentWin)) == 0) { + if ((strcmp(Tk_PathName(menuPtr->tkwin) + + strlen(Tk_PathName(parentWin)), ".apple") == 0) + || (strcmp(Tk_PathName(menuPtr->tkwin) + + strlen(Tk_PathName(parentWin)), ".help") == 0)) { + if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) { + Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL); + menuBarFlags |= MENUBAR_REDRAW_PENDING; + } + } + } + } + + menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureMacintoshMenu, menuPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkpDestroyMenu -- + * + * Destroys platform-specific menu structures. + * + * Results: + * None. + * + * Side effects: + * All platform-specific allocations are freed up. + * + *---------------------------------------------------------------------- + */ + +void +TkpDestroyMenu( + TkMenu *menuPtr) /* The common menu structure */ +{ + MenuRef macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl; + + if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) { + Tcl_CancelIdleCall(ReconfigureMacintoshMenu, menuPtr); + menuPtr->menuFlags &= ~MENU_RECONFIGURE_PENDING; + } + if (GetMenuID(macMenuHdl) == currentHelpMenuID) { + MenuRef helpMenuHdl; + MenuItemIndex helpIndex; + + if ((HMGetHelpMenu(&helpMenuHdl,&helpIndex) == noErr) + && (helpMenuHdl != NULL)) { + int i, count = CountMenuItems(helpMenuHdl); + + for (i = helpIndex; i <= count; i++) { + DeleteMenuItem(helpMenuHdl, helpIndex); + } + } + currentHelpMenuID = 0; + } + if (menuPtr->platformData != NULL) { + MenuID menuID = GetMenuID(macMenuHdl); + + DeleteMenu(menuID); + TkMacOSXFreeMenuID(menuID); + DisposeMenu(macMenuHdl); + ckfree(menuPtr->platformData); + menuPtr->platformData = NULL; + } +} + +/* + *---------------------------------------------------------------------- + * + * SetMenuCascade -- + * + * Does any cleanup to change a menu from a normal to a cascade. + * + * Results: + * Standard Tcl error. + * + * Side effects: + * The mac menu id is reset. + * + *---------------------------------------------------------------------- + */ + +int +SetMenuCascade( + TkMenu *menuPtr) /* The menu we are setting up to be a + * cascade. */ +{ + MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl; + MenuID newMenuID, menuID = GetMenuID(macMenuHdl); + int error = TCL_OK; + + if (menuID >= 256) { + error = TkMacOSXGetNewMenuID(menuPtr->interp, menuPtr, 1, &newMenuID); + if (error == TCL_OK) { + TkMacOSXFreeMenuID(menuID); + SetMenuID(macMenuHdl,newMenuID); + } + } + return error; +} + +/* + *---------------------------------------------------------------------- + * + * TkpDestroyMenuEntry -- + * + * Cleans up platform-specific menu entry items. + * + * Results: + * None + * + * Side effects: + * All platform-specific allocations are freed up. + * + *---------------------------------------------------------------------- + */ + +void +TkpDestroyMenuEntry( + TkMenuEntry *mePtr) /* The common structure for the menu entry. */ +{ + TkMenu *menuPtr = mePtr->menuPtr; + + ckfree(mePtr->platformEntryData); + if ((menuPtr->platformData != NULL) + && !(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) { + menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureMacintoshMenu, menuPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * GetEntryText -- + * + * Given a menu entry, gives back the text that should go in it. + * Separators should be done by the caller, as they have to be handled + * specially. This is primarily used to do a substitution between "..." + * and the ellipsis character which looks nicer. + * + * Results: + * itemText points to the new text for the item. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetEntryText( + TkMenuEntry *mePtr, /* A pointer to the menu entry. */ + Tcl_DString *dStringPtr) /* The DString to put the text into. This + * will be initialized by this routine. */ +{ +#ifdef USE_TK_MDEF + const int useMDEF = ((MacMenu *) mePtr->menuPtr->platformData)->useMDEF; +#endif + int noLabel = (mePtr->labelPtr == NULL || mePtr->labelLength == 0); + + Tcl_DStringInit(dStringPtr); + if (mePtr->type == TEAROFF_ENTRY && (useMDEF || noLabel)) { + Tcl_DStringAppend(dStringPtr, "(Tear-off)", -1); + } else if (mePtr->imagePtr != NULL && (useMDEF || noLabel) && + mePtr->compound == COMPOUND_NONE) { + Tcl_DStringAppend(dStringPtr, "(Image)", -1); + } else if (mePtr->bitmapPtr != NULL && (useMDEF || noLabel) && + mePtr->compound == COMPOUND_NONE) { + Tcl_DStringAppend(dStringPtr, "(Pixmap)", -1); + } else if (noLabel) { + /* + * The Mac menu manager does not like null strings. + */ + + Tcl_DStringAppend(dStringPtr, " ", -1); + } else { + int length; + char *text = Tcl_GetStringFromObj(mePtr->labelPtr, &length); + char *dStringText; + int i; + + for (i = 0; *text; text++, i++) { + if ((*text == '.') && (*(text+1) == '.') && (*(text+2) == '.')) { + Tcl_DStringAppend(dStringPtr, menuSymbols[ELLIPSIS_SYMBOL].utf, + menuSymbols[ELLIPSIS_SYMBOL].utfLen); + i += menuSymbols[ELLIPSIS_SYMBOL].utfLen - 1; + text += 2; + } else { + Tcl_DStringSetLength(dStringPtr, + Tcl_DStringLength(dStringPtr) + 1); + dStringText = Tcl_DStringValue(dStringPtr); + dStringText[i] = *text; + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * FindMarkCharacter -- + * + * Finds the Macintosh mark character based on the font of the item. We + * calculate a good mark character based on the font that this item is + * rendered in. + * + * Results: + * Mark char. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char +FindMarkCharacter( + TkMenuEntry *mePtr) /* The entry we are finding the character + * for. */ +{ + static const char markChars[] = {kCheckCharCode, kDiamondCharCode, + kBulletCharCode, '-', kCheckCharCode}; + const char *markChar = markChars; + int i = sizeof(markChars); + Tk_Font tkfont; + + tkfont = Tk_GetFontFromObj(mePtr->menuPtr->tkwin, + (mePtr->fontPtr == NULL) ? mePtr->menuPtr->fontPtr + : mePtr->fontPtr); + + while (--i) { + if (!TkMacOSXIsCharacterMissing(tkfont, *markChar)) { + break; + } + markChar++; + } + return *markChar; +} + +/* + *---------------------------------------------------------------------- + * + * GetUtfMarkCharacter -- + * + * Get the utf8 string for the given mark character, taking into account + * the special menu font char codes. + * + * Results: + * Length of returned utf8 string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +GetUtfMarkCharacter( + char markChar, + const char **markUtfPtr) +{ + const MenuSymbol *ms = menuSymbols; + int len = 0; + + while (ms->unicode) { + if (ms->charCode && ms->charCode == markChar) { + *markUtfPtr = ms->utf; + len = ms->utfLen; + break; + } + ms++; + } + if (!len) { + static char markUtf[TCL_UTF_MAX + 1]; + + Tcl_ExternalToUtf(NULL, TkMacOSXCarbonEncoding, &markChar, 1, 0, NULL, + markUtf, TCL_UTF_MAX + 1, NULL, &len, NULL); + *markUtfPtr = markUtf; + } + return len; +} + +/* + *---------------------------------------------------------------------- + * + * ParseAccelerators -- + * + * Parse menu accelerator string. + * + * Results: + * Accelerator flags. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +ParseAccelerators( + const char **accelStringPtr, + int *modifierNumPtr, + Tcl_UniChar *modifierUniChars, + int *modifierWidth) +{ + struct Modif { + const char *name; + const size_t len; + const int flag, symbol; + }; +#define MODIF(n, f) { #n, sizeof(#n)-1, ENTRY_##f##_ACCEL, f##_SYMBOL } + static const struct Modif modifs[] = { + MODIF(Control, CONTROL), + MODIF(Ctrl, CONTROL), + MODIF(Option, OPTION), + MODIF(Opt, OPTION), + MODIF(Alt, OPTION), + MODIF(Shift, SHIFT), + MODIF(Command, COMMAND), + MODIF(Cmd, COMMAND), + MODIF(Meta, COMMAND), + { NULL, 0, 0, 0} + }; +#undef MODIF + const char *accelString = *accelStringPtr; + int flags = 0, num = 0, seen = 0, width = 0; + const struct Modif *m; + + while (1) { + m = modifs; + while (m->name) { + int l = m->len; + + if (!strncasecmp(accelString, m->name, l) && + (accelString[l] == '-' || accelString[l] == '+')) { + flags |= m->flag; + accelString += l+1; + break; + } + m++; + } + if (!m->name || !*accelString) { + break; + } + } + m = modifs; + while (m->name && num < MODIFIER_NUM) { + if (flags & m->flag && !(seen & m->flag)) { + modifierUniChars[num++] = menuSymbols[m->symbol].unicode; + width += menuSymbols[m->symbol].width; + seen |= m->flag; + } + m++; + } + *accelStringPtr = accelString; + *modifierNumPtr = num; + *modifierWidth = width; + return flags; +} + +/* + *---------------------------------------------------------------------- + * + * TkpConfigureMenuEntry -- + * + * Processes configurations for menu entries. + * + * 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. */ +{ + TkMenu *menuPtr = mePtr->menuPtr; + EntryGeometry *geometryPtr = (EntryGeometry *) mePtr->platformEntryData; + + /* + * Cascade menus have to have menu IDs of less than 256. So we need to + * change the child menu if this has been configured for a cascade item. + */ + + if (mePtr->type == CASCADE_ENTRY) { + if ((mePtr->childMenuRefPtr != NULL) + && (mePtr->childMenuRefPtr->menuPtr != NULL)) { + MenuHandle childMenuHdl = ((MacMenu *) mePtr + ->childMenuRefPtr->menuPtr->platformData)->menuHdl; + + if (childMenuHdl != NULL) { + int error = SetMenuCascade(mePtr->childMenuRefPtr->menuPtr); + + if (error != TCL_OK) { + return error; + } + + if (menuPtr->menuType == MENUBAR) { + CFStringRef cfStr = CFStringCreateWithCString(NULL, + (!(mePtr->labelPtr) ? "" : + Tcl_GetString(mePtr->labelPtr)), + kCFStringEncodingUTF8); + + if (cfStr) { + SetMenuTitleWithCFString(childMenuHdl, cfStr); + CFRelease(cfStr); + } + } + } + } + } + + /* + * We need to parse the accelerator string. If it has the strings for + * Command, Control, Shift or Option, we need to flag it so we can draw + * the symbols for it. We also need to precalcuate the position of the + * first real character we are drawing. + */ + + if (0 == mePtr->accelLength) { + geometryPtr->accelTextStart = -1; + } else { + const char *accelString = (mePtr->accelPtr == NULL) ? "" + : Tcl_GetString(mePtr->accelPtr); + const char *accelStart = accelString; + + mePtr->entryFlags &= ~ENTRY_ACCEL_MASK; + mePtr->entryFlags |= ParseAccelerators(&accelString, + &geometryPtr->modifierNum, geometryPtr->modifierUniChars, + &geometryPtr->modifierWidth); + geometryPtr->accelTextStart = (ptrdiff_t)(accelString - accelStart); + } + + if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) { + menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureMacintoshMenu, menuPtr); + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * ReconfigureIndividualMenu -- + * + * This routine redoes the guts of the menu. It works from a base item + * and offset, so that a regular menu will just have all of its items + * added, but the help menu will have all of its items appended after the + * apple-defined items. + * + * Results: + * None. + * + * Side effects: + * The Macintosh menu handle is updated + * + *---------------------------------------------------------------------- + */ + +void +ReconfigureIndividualMenu( + TkMenu *menuPtr, /* The menu we are affecting. */ + MenuHandle macMenuHdl, /* The macintosh menu we are affecting. Will + * not necessarily be menuPtr->platformData + * because this could be the help menu. */ + int base) /* The last index that we do not want touched. + * 0 for normal menus; # of system help menu + * items for help menus. */ +{ + int count; + int index; + TkMenuEntry *mePtr; + int parentDisabled = 0; + +#ifdef TK_MAC_DEBUG_MENUS + /* + * Carbon-internal menu debugging (c.f. Technote 2124) + */ + + TkMacOSXInitNamedDebugSymbol(HIToolbox, void, DebugPrintMenu, + MenuRef menu); + if (DebugPrintMenu) { + DebugPrintMenu(macMenuHdl); + } +#endif + + mePtr = GetParentMenuEntry(menuPtr); + if (mePtr && mePtr->state == ENTRY_DISABLED) { + parentDisabled = 1; + } + + /* + * First, we get rid of all of the old items. + */ + + count = CountMenuItems(macMenuHdl); + for (index = base; index < count; index++) { + DeleteMenuItem(macMenuHdl, base + 1); + } + + count = menuPtr->numEntries; + + for (index = 1; index <= count; index++) { + mePtr = menuPtr->entries[index - 1]; + + /* + * We have to do separators separately because SetMenuItemText does + * not parse meta-characters. + */ + + if (mePtr->type == SEPARATOR_ENTRY) { + AppendMenuItemTextWithCFString(macMenuHdl, NULL, + kMenuItemAttrSeparator | kMenuItemAttrDisabled, 0, NULL); + } else { + Tcl_DString itemTextDString; + CFStringRef cfStr; + + GetEntryText(mePtr, &itemTextDString); + cfStr = CFStringCreateWithCString(NULL, + Tcl_DStringValue(&itemTextDString), kCFStringEncodingUTF8); + if (cfStr) { + AppendMenuItemTextWithCFString(macMenuHdl, cfStr, 0, 0, NULL); + CFRelease(cfStr); + } else { + AppendMenuItemTextWithCFString(macMenuHdl, CFSTR ("<Error>"), + 0, 0, NULL); + } + Tcl_DStringFree(&itemTextDString); + + /* + * Set enabling and disabling correctly. + */ + + if (parentDisabled || (mePtr->state == ENTRY_DISABLED)) { + DisableMenuItem(macMenuHdl, base + index); + } else { + EnableMenuItem(macMenuHdl, base + index); + } + + /* + * Set the check mark for check entries and radio entries. + */ + + SetItemMark(macMenuHdl, base + index, 0); + if ((mePtr->type == CHECK_BUTTON_ENTRY) + || (mePtr->type == RADIO_BUTTON_ENTRY)) { + CheckMenuItem(macMenuHdl, base + index, (mePtr->entryFlags + & ENTRY_SELECTED) && mePtr->indicatorOn); + if (mePtr->indicatorOn + && (mePtr->entryFlags & ENTRY_SELECTED)) { + SetItemMark(macMenuHdl, base + index, + FindMarkCharacter(mePtr)); + } + } + + if (mePtr->type == CASCADE_ENTRY) { + if ((mePtr->childMenuRefPtr != NULL) + && (mePtr->childMenuRefPtr->menuPtr != NULL)) { + MenuHandle childMenuHdl = + ((MacMenu *) mePtr->childMenuRefPtr + ->menuPtr->platformData)->menuHdl; + + if (childMenuHdl != NULL) { + ChkErr(SetMenuItemHierarchicalID, macMenuHdl, + base + index, GetMenuID(childMenuHdl)); + } + + /* + * If we changed the highligthing of this menu, its + * children all have to be reconfigured so that their + * state will be reflected in the menubar. + */ + + if (!(mePtr->childMenuRefPtr->menuPtr->menuFlags + & MENU_RECONFIGURE_PENDING)) { + mePtr->childMenuRefPtr->menuPtr->menuFlags + |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureMacintoshMenu, + mePtr->childMenuRefPtr->menuPtr); + } + } + } + + if ((mePtr->type != CASCADE_ENTRY) && (mePtr->accelPtr != NULL)) { + int accelLen, modifiers = 0, hasCmd = 0; + EntryGeometry *geometryPtr = (EntryGeometry *) + mePtr->platformEntryData; + int offset = geometryPtr->accelTextStart; + char *accel = Tcl_GetStringFromObj(mePtr->accelPtr, &accelLen); + + accelLen -= offset; + accel += offset; + if (mePtr->entryFlags & ENTRY_OPTION_ACCEL) { + modifiers |= kMenuOptionModifier; + } + if (mePtr->entryFlags & ENTRY_SHIFT_ACCEL) { + modifiers |= kMenuShiftModifier; + } + if (mePtr->entryFlags & ENTRY_CONTROL_ACCEL) { + modifiers |= kMenuControlModifier; + } + if (mePtr->entryFlags & ENTRY_COMMAND_ACCEL) { + hasCmd = 1; + } + if (accelLen == 1) { + if (hasCmd || (modifiers != 0 && modifiers != + kMenuShiftModifier)) { + SetItemCmd(macMenuHdl, base + index, accel[0]); + if (!hasCmd) { + modifiers |= kMenuNoCommandModifier; + } + } + } else { + /* + * Convert from accelerator names to Carbon menu glyphs. + */ + + struct Glyph { + const char *name; + const size_t len; + const char glyph; + }; +#define GLYPH(n, g) { #n, sizeof(#n)-1, kMenu##g##Glyph } + static const struct Glyph glyphs[] = { + GLYPH(PageUp, PageUp), + GLYPH(PageDown, PageDown), + GLYPH(Left, LeftArrow), + GLYPH(Right, RightArrow), + GLYPH(Up, UpArrow), + GLYPH(Down, DownArrow), + GLYPH(Escape, Escape), + GLYPH(Clear, Clear), + GLYPH(Enter, Enter), + GLYPH(Backspace,DeleteLeft), + GLYPH(Space, Space), + GLYPH(Tab, TabRight), + GLYPH(Delete, DeleteRight), + GLYPH(Home, NorthwestArrow), + GLYPH(End, SoutheastArrow), + GLYPH(Return, Return), + GLYPH(Help, Help), + GLYPH(Power, Power), + { NULL, 0, 0} + }; +#undef GLYPH + const struct Glyph *g = glyphs; + char glyph = 0; + + if (accel[0] == 'F' && accelLen < 4 && + (accel[1] > '0' && accel[1] <= '9')) { + int fkey = accel[1] - '0'; + + if (accelLen == 3) { + if (accel[2] >= '0' && accel[2] <= '9') { + fkey = 10 * fkey + (accel[2] - '0'); + } else { + fkey = 0; + } + } + if (fkey >= 1 && fkey <= 12) { + glyph = kMenuF1Glyph + fkey - 1; + } else if (fkey >= 13 && fkey <= 15) { + glyph = kMenuF13Glyph + fkey - 13; + } + } else while (g->name) { + if (accel[0] == g->name[0] && + (size_t)accelLen == g->len && + !strncasecmp(accel, g->name, g->len)) { + glyph = g->glyph; + break; + } + g++; + } + if (glyph) { + ChkErr(SetMenuItemKeyGlyph, macMenuHdl, base + index, + glyph); + if (!hasCmd) { + modifiers |= kMenuNoCommandModifier; + } + geometryPtr->accelGlyph = glyph; + } + } + ChkErr(SetMenuItemModifiers, macMenuHdl, base + index, + modifiers); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * ReconfigureMacintoshMenu -- + * + * Rebuilds the Macintosh MenuHandle items from the menu. Called usually + * as an idle handler, but can be called synchronously if the menu is + * about to be posted. + * + * Results: + * None. + * + * Side effects: + * Configuration information get set for mePtr; old resources get freed, + * if any need it. + * + *---------------------------------------------------------------------- + */ + +void +ReconfigureMacintoshMenu( + ClientData clientData) /* Information about menu entry; may or may + * not already have values for some fields. */ +{ + TkMenu *menuPtr = clientData; + MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl; + MenuHandle helpMenuHdl = NULL; + + menuPtr->menuFlags &= ~MENU_RECONFIGURE_PENDING; + + if (NULL == macMenuHdl) { + return; + } + + ReconfigureIndividualMenu(menuPtr, macMenuHdl, 0); + + if (GetMenuID(macMenuHdl) == currentHelpMenuID) { + MenuItemIndex helpIndex; + + HMGetHelpMenu(&helpMenuHdl,&helpIndex); + if (helpMenuHdl != NULL) { + ReconfigureIndividualMenu(menuPtr, helpMenuHdl, helpIndex - 1); + } + } + + if (menuPtr->menuType == MENUBAR) { + if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) { + Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL); + menuBarFlags |= MENUBAR_REDRAW_PENDING; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * CompleteIdlers -- + * + * Completes all idle handling so that the menus are in sync when the + * user invokes them with the mouse. + * + * Results: + * None. + * + * Side effects: + * The Macintosh menu handles are flushed out. + * + *---------------------------------------------------------------------- + */ + +void +CompleteIdlers( + TkMenu *menuPtr) /* The menu we are completing. */ +{ + int i; + + if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) { + Tcl_CancelIdleCall(ReconfigureMacintoshMenu, menuPtr); + ReconfigureMacintoshMenu(menuPtr); + } + + for (i = 0; i < menuPtr->numEntries; i++) { + if ((menuPtr->entries[i]->type == CASCADE_ENTRY) && + (menuPtr->entries[i]->childMenuRefPtr != NULL) && + (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) { + CompleteIdlers(menuPtr->entries[i]->childMenuRefPtr->menuPtr); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpPostMenu -- + * + * Posts a menu on the screen + * + * Results: + * None. + * + * Side effects: + * The menu is posted and handled. + * + *---------------------------------------------------------------------- + */ + +int +TkpPostMenu( + Tcl_Interp *interp, /* The interpreter this menu lives in */ + TkMenu *menuPtr, /* The menu we are posting */ + int x, /* The global x-coordinate of the top, left- + * hand corner of where the menu is supposed + * to be posted. */ + int y) /* The global y-coordinate */ +{ + MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl; + long popUpResult; + int result; + + if (inPostMenu > 0) { + Tcl_AppendResult(interp, + "Cannot call post menu while already posting menu", NULL); + result = TCL_ERROR; + } else { + short menuID; + Window window; + int oldWidth = menuPtr->totalWidth; + + inPostMenu++; + result = TkPreprocessMenu(menuPtr); + + /* + * The post commands could have deleted the menu, which means we are + * dead and should go away. + */ + + if (result != TCL_OK || !menuPtr->tkwin) { + goto endPostMenu; + } + + CompleteIdlers(menuPtr); + if (menuBarFlags & MENUBAR_REDRAW_PENDING) { + Tcl_CancelIdleCall(DrawMenuBarWhenIdle, NULL); + DrawMenuBarWhenIdle(NULL); + } + RecursivelyInsertMenu(menuPtr); + + TkMacOSXTrackingLoop(1); + popUpResult = PopUpMenuSelect(macMenuHdl, y, x, menuPtr->active); + TkMacOSXTrackingLoop(0); + menuPtr->totalWidth = oldWidth; + + /* + * Simulate the mouse up. + */ + + window = Tk_WindowId(menuPtr->tkwin); + TkGenerateButtonEventForXPointer(window); + + /* + * Dispatch the command. + */ + + menuID = HiWord(popUpResult); + if (menuID != 0) { + result = TkMacOSXDispatchMenuEvent(menuID, LoWord(popUpResult)); + } + +endPostMenu: + inPostMenu--; + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TkpMenuNewEntry -- + * + * Adds a pointer to a new menu entry structure with the platform- + * specific fields filled in. The Macintosh uses the platformEntryData + * field of the TkMenuEntry record to store geometry information. + * + * Results: + * Standard TCL error. + * + * Side effects: + * Storage gets allocated. New menu entry data is put into the + * platformEntryData field of the mePtr. + * + *---------------------------------------------------------------------- + */ + +int +TkpMenuNewEntry( + TkMenuEntry *mePtr) /* The menu we are adding an entry to */ +{ + EntryGeometry *geometryPtr = ckalloc(sizeof(EntryGeometry)); + TkMenu *menuPtr = mePtr->menuPtr; + + geometryPtr->accelTextStart = 0; + geometryPtr->accelTextWidth = 0; + geometryPtr->nonAccelMargin = 0; + geometryPtr->modifierWidth = 0; + geometryPtr->modifierNum = 0; + geometryPtr->accelGlyph = 0; + mePtr->platformEntryData = (TkMenuPlatformEntryData) geometryPtr; + if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) { + menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureMacintoshMenu, menuPtr); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_MacOSXTurnOffMenus -- + * + * Turns off all the menu drawing code. This is more than just disabling + * the "menu" command, this means that Tk will NEVER touch the menubar. + * It is needed in the Plugin, where Tk does not own the menubar. + * + * Results: + * None. + * + * Side effects: + * A flag is set which will disable all menu drawing. + * + *---------------------------------------------------------------------- + */ + +void +Tk_MacOSXTurnOffMenus(void) +{ + gNoTkMenus = 1; +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuBarWhenIdle -- + * + * Update the menu bar next time there is an idle event. + * + * Results: + * None. + * + * Side effects: + * Menu bar is redrawn. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuBarWhenIdle( + ClientData clientData) /* ignored here */ +{ + TkMenuReferences *menuRefPtr; + TkMenu *appleMenuPtr, *helpMenuPtr, *menuBarPtr = NULL; + MenuHandle macMenuHdl; + Tcl_HashEntry *hashEntryPtr; + + /* + * If we have been turned off, exit. + */ + + if (gNoTkMenus) { + return; + } + + /* + * We need to clear the apple and help menus of any extra items. + */ + + if (currentAppleMenuID != 0) { + hashEntryPtr = Tcl_FindHashEntry(&commandTable, + (char*)(intptr_t)currentAppleMenuID); + appleMenuPtr = Tcl_GetHashValue(hashEntryPtr); + TkpDestroyMenu(appleMenuPtr); + TkpNewMenu(appleMenuPtr); + appleMenuPtr->menuFlags &= ~MENU_APPLE_MENU; + appleMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureMacintoshMenu, appleMenuPtr); + } + + if (currentHelpMenuID != 0) { + hashEntryPtr = Tcl_FindHashEntry(&commandTable, + (char*)(intptr_t)currentHelpMenuID); + helpMenuPtr = Tcl_GetHashValue(hashEntryPtr); + TkpDestroyMenu(helpMenuPtr); + TkpNewMenu(helpMenuPtr); + helpMenuPtr->menuFlags &= ~MENU_HELP_MENU; + helpMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureMacintoshMenu, helpMenuPtr); + } + + /* + * We need to find the clone of this menu that is the menubar. Once we do + * that, for every cascade in the menu, we need to insert the Mac menu in + * the Mac menubar. Finally, we need to redraw the menubar. + */ + + menuRefPtr = NULL; + if (currentMenuBarName != NULL) { + menuRefPtr = TkFindMenuReferences(currentMenuBarInterp, + currentMenuBarName); + } + if (menuRefPtr) { + TkMenu *menuPtr; + TkMenu *cascadeMenuPtr; + char *appleMenuName, *helpMenuName; + int appleIndex = -1, helpIndex = -1, i; + + menuPtr = menuRefPtr->menuPtr; + if (menuPtr != NULL) { + TkMenuReferences *specialMenuRefPtr; + TkMenuEntry *specialEntryPtr; + + appleMenuName = ckalloc(strlen(currentMenuBarName) + 1 + + strlen(".apple") + 1); + sprintf(appleMenuName, "%s.apple", Tk_PathName(menuPtr->tkwin)); + specialMenuRefPtr = TkFindMenuReferences(currentMenuBarInterp, + appleMenuName); + if ((specialMenuRefPtr != NULL) + && (specialMenuRefPtr->menuPtr != NULL)) { + for (specialEntryPtr = specialMenuRefPtr->parentEntryPtr; + specialEntryPtr != NULL; + specialEntryPtr = specialEntryPtr->nextCascadePtr) { + if (specialEntryPtr->menuPtr == menuPtr) { + appleIndex = specialEntryPtr->index; + break; + } + } + } + ckfree(appleMenuName); + + helpMenuName = ckalloc(strlen(currentMenuBarName) + 1 + + strlen(".help") + 1); + sprintf(helpMenuName, "%s.help", Tk_PathName(menuPtr->tkwin)); + specialMenuRefPtr = TkFindMenuReferences(currentMenuBarInterp, + helpMenuName); + if ((specialMenuRefPtr != NULL) + && (specialMenuRefPtr->menuPtr != NULL)) { + for (specialEntryPtr = specialMenuRefPtr->parentEntryPtr; + specialEntryPtr != NULL; + specialEntryPtr = specialEntryPtr->nextCascadePtr) { + if (specialEntryPtr->menuPtr == menuPtr) { + helpIndex = specialEntryPtr->index; + break; + } + } + } + ckfree(helpMenuName); + } + + for (menuBarPtr = menuPtr; + (menuBarPtr != NULL) && (menuBarPtr->menuType != MENUBAR); + menuBarPtr = menuBarPtr->nextInstancePtr) { + /* + * Null loop body. + */ + } + + if (menuBarPtr) { + if (menuBarPtr->tearoff != menuPtr->tearoff) { + if (menuBarPtr->tearoff) { + appleIndex = (-1 == appleIndex) ? appleIndex + : appleIndex + 1; + helpIndex = (-1 == helpIndex) ? helpIndex + : helpIndex + 1; + } else { + appleIndex = (-1 == appleIndex) ? appleIndex + : appleIndex - 1; + helpIndex = (-1 == helpIndex) ? helpIndex + : helpIndex - 1; + } + } + ClearMenuBar(); + + if (appleIndex == -1) { + InsertMenu(tkAppleMenu, 0); + currentAppleMenuID = 0; + tkCurrentAppleMenu = tkAppleMenu; + } else { + short appleID; + + appleMenuPtr = menuBarPtr->entries[appleIndex] + ->childMenuRefPtr->menuPtr; + TkpDestroyMenu(appleMenuPtr); + TkMacOSXGetNewMenuID(appleMenuPtr->interp, appleMenuPtr, 0, + &appleID); + macMenuHdl = NewMenu(appleID, "\p\024"); + appleMenuPtr->platformData = ckalloc(sizeof(MacMenu)); + ((MacMenu *)appleMenuPtr->platformData)->menuHdl + = macMenuHdl; + appleMenuPtr->menuFlags |= MENU_APPLE_MENU; + if (!(appleMenuPtr->menuFlags + & MENU_RECONFIGURE_PENDING)) { + appleMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureMacintoshMenu, appleMenuPtr); + } + InsertMenu(macMenuHdl, 0); + RecursivelyInsertMenu(appleMenuPtr); + currentAppleMenuID = appleID; + tkCurrentAppleMenu = macMenuHdl; + } + if (helpIndex == -1) { + currentHelpMenuID = 0; + } + + for (i = 0; i < menuBarPtr->numEntries; i++) { + if (i == appleIndex) { + if (menuBarPtr->entries[i]->state == ENTRY_DISABLED) { + DisableMenuItem(((MacMenu *) menuBarPtr->entries[i] + ->childMenuRefPtr->menuPtr + ->platformData)->menuHdl, 0); + } else { + EnableMenuItem(((MacMenu *) menuBarPtr->entries[i] + ->childMenuRefPtr->menuPtr + ->platformData)->menuHdl, 0); + } + continue; + } else if (i == helpIndex) { + TkMenu *helpMenuPtr = menuBarPtr->entries[i] + ->childMenuRefPtr->menuPtr; + + if (helpMenuPtr == NULL) { + continue; + } + helpMenuPtr->menuFlags |= MENU_HELP_MENU; + if (!(helpMenuPtr->menuFlags + & MENU_RECONFIGURE_PENDING)) { + helpMenuPtr->menuFlags + |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureMacintoshMenu, helpMenuPtr); + } + macMenuHdl = + ((MacMenu *) helpMenuPtr->platformData)->menuHdl; + currentHelpMenuID = GetMenuID(macMenuHdl); + } else if (menuBarPtr->entries[i]->type + == CASCADE_ENTRY) { + if ((menuBarPtr->entries[i]->childMenuRefPtr != NULL) + && menuBarPtr->entries[i]->childMenuRefPtr + ->menuPtr != NULL) { + cascadeMenuPtr = menuBarPtr->entries[i] + ->childMenuRefPtr->menuPtr; + macMenuHdl = ((MacMenu *) cascadeMenuPtr + ->platformData)->menuHdl; + DeleteMenu(GetMenuID(macMenuHdl)); + InsertMenu(macMenuHdl, 0); + RecursivelyInsertMenu(cascadeMenuPtr); + if (menuBarPtr->entries[i]->state == ENTRY_DISABLED) { + DisableMenuItem(((MacMenu *) menuBarPtr->entries[i] + ->childMenuRefPtr->menuPtr + ->platformData)->menuHdl, 0); + } else { + EnableMenuItem(((MacMenu *) menuBarPtr->entries[i] + ->childMenuRefPtr->menuPtr + ->platformData)->menuHdl, 0); + } + } + } + } + } + } + if (!menuRefPtr || !menuBarPtr) { + SetDefaultMenubar(); + } + DrawMenuBar(); + menuBarFlags &= ~MENUBAR_REDRAW_PENDING; +} + +/* + *---------------------------------------------------------------------- + * + * RecursivelyInsertMenu -- + * + * Puts all of the cascades of this menu in the Mac hierarchical list. + * + * Results: + * None. + * + * Side effects: + * The menubar is changed. + * + *---------------------------------------------------------------------- + */ + +void +RecursivelyInsertMenu( + TkMenu *menuPtr) /* All of the cascade items in this menu will + * be inserted into the mac menubar. */ +{ + int i; + TkMenu *cascadeMenuPtr; + MenuHandle macMenuHdl; + + for (i = 0; i < menuPtr->numEntries; i++) { + if (menuPtr->entries[i]->type == CASCADE_ENTRY) { + if ((menuPtr->entries[i]->childMenuRefPtr != NULL) && + (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) { + cascadeMenuPtr = menuPtr->entries[i]->childMenuRefPtr->menuPtr; + macMenuHdl = + ((MacMenu *) cascadeMenuPtr->platformData)->menuHdl; + InsertMenu(macMenuHdl, -1); + RecursivelyInsertMenu(cascadeMenuPtr); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * RecursivelyDeleteMenu -- + * + * Takes all of the cascades of this menu out of the Mac hierarchical + * list. + * + * Results: + * None. + * + * Side effects: + * The menubar is changed. + * + *---------------------------------------------------------------------- + */ + +void +RecursivelyDeleteMenu( + TkMenu *menuPtr) /* All of the cascade items in this menu will + * be deleted from the mac menubar. */ +{ + int i; + TkMenu *cascadeMenuPtr; + MenuHandle macMenuHdl; + + for (i = 0; i < menuPtr->numEntries; i++) { + if (menuPtr->entries[i]->type == CASCADE_ENTRY) { + if ((menuPtr->entries[i]->childMenuRefPtr != NULL) && + (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) { + cascadeMenuPtr = menuPtr->entries[i]->childMenuRefPtr->menuPtr; + macMenuHdl = + ((MacMenu *) cascadeMenuPtr->platformData)->menuHdl; + DeleteMenu(GetMenuID(macMenuHdl)); + RecursivelyDeleteMenu(cascadeMenuPtr); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * SetDefaultMenubar -- + * + * Puts the Apple, File and Edit menus into the Macintosh menubar. + * + * Results: + * None. + * + * Side effects: + * The menubar is changed. + * + *---------------------------------------------------------------------- + */ + +void +SetDefaultMenubar(void) +{ + if (currentMenuBarName != NULL) { + ckfree(currentMenuBarName); + currentMenuBarName = NULL; + } + currentMenuBarOwner = NULL; + ClearMenuBar(); + InsertMenu(tkAppleMenu, 0); + InsertMenu(tkFileMenu, 0); + InsertMenu(tkEditMenu, 0); + if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) { + Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL); + menuBarFlags |= MENUBAR_REDRAW_PENDING; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpSetMainMenubar -- + * + * Puts the menu associated with a window into the menubar. Should only + * be called when the window is in front. + * + * Results: + * None. + * + * Side effects: + * The menubar is changed. + * + *---------------------------------------------------------------------- + */ + +void +TkpSetMainMenubar( + Tcl_Interp *interp, /* The interpreter of the application */ + Tk_Window tkwin, /* The frame we are setting up */ + const char *menuName) /* The name of the menu to put in front. If + * NULL, use the default menu bar. */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + WindowRef macWindowPtr; + WindowRef frontNonFloating; + + macWindowPtr = TkMacOSXDrawableWindow(winPtr->window); + + frontNonFloating = ActiveNonFloatingWindow(); + if ((macWindowPtr == NULL) || (macWindowPtr != frontNonFloating)) { + return; + } + + if ((currentMenuBarInterp != interp) || (currentMenuBarOwner != tkwin) + || (currentMenuBarName == NULL) || (menuName == NULL) + || (strcmp(menuName, currentMenuBarName) != 0)) { + Tk_Window searchWindow; + TopLevelMenubarList *listPtr; + + if (currentMenuBarName != NULL) { + ckfree(currentMenuBarName); + } + + if (menuName == NULL) { + searchWindow = tkwin; + if (strcmp(Tk_Class(searchWindow), "Menu") == 0) { + TkMenuReferences *menuRefPtr; + + menuRefPtr = TkFindMenuReferences(interp, Tk_PathName(tkwin)); + if (menuRefPtr != NULL) { + TkMenu *menuPtr = menuRefPtr->menuPtr; + + if (menuPtr != NULL) { + searchWindow = menuPtr->masterMenuPtr->tkwin; + } + } + } + for (; searchWindow != NULL; + searchWindow = Tk_Parent(searchWindow)) { + for (listPtr = windowListPtr; listPtr != NULL; + listPtr = listPtr->nextPtr) { + if (listPtr->tkwin == searchWindow) { + break; + } + } + if (listPtr != NULL) { + menuName = Tk_PathName( + listPtr->menuPtr->masterMenuPtr->tkwin); + break; + } + } + } + + if (menuName == NULL) { + currentMenuBarName = NULL; + } else { + currentMenuBarName = ckalloc(strlen(menuName) + 1); + strcpy(currentMenuBarName, menuName); + } + currentMenuBarOwner = tkwin; + currentMenuBarInterp = interp; + } + if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) { + Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL); + menuBarFlags |= MENUBAR_REDRAW_PENDING; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpSetWindowMenuBar -- + * + * Associates a given menu with a window. + * + * Results: + * None. + * + * Side effects: + * On Windows and UNIX, associates the platform menu with the platform + * window. + * + *---------------------------------------------------------------------- + */ + +void +TkpSetWindowMenuBar( + Tk_Window tkwin, /* The window we are setting the menu in */ + TkMenu *menuPtr) /* The menu we are setting */ +{ + TopLevelMenubarList *listPtr, *prevPtr; + + /* + * Remove any existing reference to this window. + */ + + for (prevPtr = NULL, listPtr = windowListPtr; + listPtr != NULL; + prevPtr = listPtr, listPtr = listPtr->nextPtr) { + if (listPtr->tkwin == tkwin) { + break; + } + } + + if (listPtr != NULL) { + if (prevPtr != NULL) { + prevPtr->nextPtr = listPtr->nextPtr; + } else { + windowListPtr = listPtr->nextPtr; + } + ckfree(listPtr); + } + + if (menuPtr != NULL) { + listPtr = ckalloc(sizeof(TopLevelMenubarList)); + listPtr->nextPtr = windowListPtr; + windowListPtr = listPtr; + listPtr->tkwin = tkwin; + listPtr->menuPtr = menuPtr; + } +} + +/* + *---------------------------------------------------------------------- + * + * EventuallyInvokeMenu -- + * + * This IdleTime callback actually invokes the menu command scheduled in + * TkMacOSXDispatchMenuEvent. + * + * Results: + * None. + * + * Side effects: + * Commands get executed. + * + *---------------------------------------------------------------------- + */ + +void +EventuallyInvokeMenu ( + ClientData data) +{ + struct MenuCommandHandlerData *realData = data; + int code; + + code = TkInvokeMenu(realData->menuPtr->interp, realData->menuPtr, + realData->index); + + if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) { + Tcl_AddErrorInfo(realData->menuPtr->interp, "\n (menu invoke)"); + Tcl_BackgroundException(realData->menuPtr->interp, code); + } + + if (realData->menuPtr->tkwin) { + RecursivelyClearActiveMenu(realData->menuPtr); + } + TkMacOSXClearMenubarActive(); + + Tcl_Release(realData->menuPtr->interp); + Tcl_Release(realData->menuPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXDispatchMenuEvent -- + * + * Given a menu id and an item, dispatches the command associated with + * it. + * + * Results: + * None. + * + * Side effects: + * Commands for the event are scheduled for execution at idle time. + * + *---------------------------------------------------------------------- + */ + +int +TkMacOSXDispatchMenuEvent( + int menuID, /* The menu id of the menu we are invoking */ + int index) /* The one-based index of the item that was + * selected. */ +{ + int result = TCL_OK; + + if (menuID != 0) { + if (menuID == kHMHelpMenuID) { + if (currentMenuBarOwner != NULL) { + TkMenuReferences *helpMenuRef; + char *helpMenuName = ckalloc(strlen(currentMenuBarName) + + strlen(".help") + 1); + + sprintf(helpMenuName, "%s.help", currentMenuBarName); + helpMenuRef = TkFindMenuReferences(currentMenuBarInterp, + helpMenuName); + ckfree(helpMenuName); + if ((helpMenuRef != NULL) && (helpMenuRef->menuPtr != NULL)) { + MenuRef outHelpMenu; + MenuItemIndex itemIndex; + int newIndex; + + HMGetHelpMenu(&outHelpMenu, &itemIndex); + newIndex = index - itemIndex; + result = TkInvokeMenu(currentMenuBarInterp, + helpMenuRef->menuPtr, newIndex); + } + } + } else { + Tcl_HashEntry *commandEntryPtr = + Tcl_FindHashEntry(&commandTable, (char*)(intptr_t)menuID); + if (commandEntryPtr != NULL) { + TkMenu *menuPtr = (TkMenu *) Tcl_GetHashValue(commandEntryPtr); + + if ((currentAppleMenuID == menuID) + && (index > menuPtr->numEntries + 1)) { + /* + * We don't need to do anything here, the standard + * Application event handler will open the built-in Apple + * menu item for us. + */ + + result = TCL_OK; + } else { + struct MenuCommandHandlerData *data = + ckalloc(sizeof(struct MenuCommandHandlerData)); + + Tcl_Preserve(menuPtr->interp); + Tcl_Preserve(menuPtr); + data->menuPtr = menuPtr; + data->index = index - 1; + Tcl_DoWhenIdle(EventuallyInvokeMenu, data); + /* result = TkInvokeMenu(menuPtr->interp, menuPtr, index - 1); */ + } + } else { + return TCL_ERROR; + } + } + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * GetMenuIndicatorGeometry -- + * + * Gets the width and height of the indicator area of a menu. + * + * Results: + * widthPtr and heightPtr are set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetMenuIndicatorGeometry ( + TkMenu *menuPtr, /* The menu we are drawing */ + TkMenuEntry *mePtr, /* The entry we are measuring */ + Tk_Font tkfont, /* Precalculated font */ + const Tk_FontMetrics *fmPtr,/* Precalculated font metrics */ + int *widthPtr, /* The resulting width */ + int *heightPtr) /* The resulting height */ +{ + *heightPtr = fmPtr->linespace + menuItemExtraHeight; + if (IS_THEME_MENU_FONT(tkfont)) { + *widthPtr = menuMarkColumnWidth; + } else { + const char markChar = FindMarkCharacter(mePtr); + const char *markUtf = NULL; + int len; + + len = GetUtfMarkCharacter(markChar, &markUtf); + *widthPtr = Tk_TextWidth(tkfont, markUtf, len) + 2*menuMarkIndent; + } +} + +/* + *---------------------------------------------------------------------- + * + * GetMenuAccelGeometry -- + * + * Gets the width and height of the accelerator area of a menu. + * + * Results: + * widthPtr and heightPtr are set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetMenuAccelGeometry ( + 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 precalculated font metrics */ + int *modWidthPtr, /* The width of all of the key + * modifier symbols. */ + int *textWidthPtr, /* The resulting width */ + int *heightPtr) /* The resulting height */ +{ + *heightPtr = fmPtr->linespace + menuItemExtraHeight; + *modWidthPtr = menuSymbols[COMMAND_SYMBOL].width; + *textWidthPtr = 0; + if (mePtr->type != CASCADE_ENTRY && mePtr->accelLength > 0) { + const char *accel = (mePtr->accelPtr == NULL) ? "" + : Tcl_GetString(mePtr->accelPtr); + EntryGeometry *geometryPtr = (EntryGeometry*)mePtr->platformEntryData; + + if (IS_THEME_MENU_FONT(tkfont)) { + CFStringRef cfStr; + int width = 0; + int maxWidth = ((TkFont *)tkfont)->fm.maxWidth; + + if (geometryPtr->accelGlyph) { + cfStr = CFStringCreateWithBytes(NULL, + (UInt8*)&geometryPtr->accelGlyph, 1, + kTextEncodingMacKeyboardGlyphs, false); + if (cfStr) { + width = MeasureThemeText(cfStr, kThemeMenuItemCmdKeyFont); + CFRelease(cfStr); + } + } + if ((mePtr->entryFlags & ENTRY_ACCEL_MASK) == 0) { + if (!geometryPtr->accelGlyph) { + width = Tk_TextWidth(tkfont, accel, mePtr->accelLength); + } + *textWidthPtr = maxWidth; + if (width < maxWidth) { + *modWidthPtr = 0; + } else { + *modWidthPtr = width - maxWidth; + } + } else { + if (!geometryPtr->accelGlyph) { + width = Tk_TextWidth(tkfont, accel + + geometryPtr->accelTextStart, mePtr->accelLength - + geometryPtr->accelTextStart); + } + if (width < maxWidth) { + *textWidthPtr = maxWidth; + } else { + *textWidthPtr = width; + } + if (geometryPtr->modifierNum) { + *modWidthPtr = geometryPtr->modifierWidth; + } + } + } else { + *textWidthPtr = Tk_TextWidth(tkfont, accel, mePtr->accelLength); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * GetTearoffEntryGeometry -- + * + * Gets the width and height of of a tearoff entry. + * + * Results: + * widthPtr and heightPtr are set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetTearoffEntryGeometry ( + 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 */ +{ +#ifdef USE_TK_MDEF + const int useMDEF = ((MacMenu *) menuPtr->platformData)->useMDEF; +#endif + if (useMDEF && menuPtr->menuType != TEAROFF_MENU) { + *heightPtr = fmPtr->linespace + menuItemExtraHeight; + *widthPtr = menuPtr->totalWidth; + } else { + *widthPtr = *heightPtr = 0; + } +} + +/* + *---------------------------------------------------------------------- + * + * GetMenuSeparatorGeometry -- + * + * Gets the width and height of menu separator. + * + * Results: + * widthPtr and heightPtr are set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetMenuSeparatorGeometry( + 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 precalcualted font metrics */ + int *widthPtr, /* The resulting width */ + int *heightPtr) /* The resulting height */ +{ + *widthPtr = 0; + *heightPtr = menuSeparatorHeight; +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuEntryIndicator -- + * + * This procedure draws the indicator part of a menu. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its + * current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuEntryIndicator( + TkMenu *menuPtr, /* The menu we are drawing */ + TkMenuEntry *mePtr, /* The entry we are drawing */ + Drawable d, /* The drawable we are drawing */ + GC gc, /* The GC we are drawing with */ + GC indicatorGC, /* The GC to use for the indicator */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + int x, /* topleft hand corner of entry */ + int y, /* topleft hand corner of entry */ + int width, /* width of entry */ + int height) /* height of entry */ +{ + if ((mePtr->type == CHECK_BUTTON_ENTRY) || + (mePtr->type == RADIO_BUTTON_ENTRY)) { + if (mePtr->indicatorOn && (mePtr->entryFlags & ENTRY_SELECTED)) { + short mark; + int baseline = y + (height + fmPtr->ascent - fmPtr->descent)/2; + + GetItemMark(((MacMenu *) menuPtr->platformData)->menuHdl, + mePtr->index + 1, &mark); + if (IS_THEME_MENU_FONT(tkfont)) { + ThemeFontID font = kThemeMenuItemMarkFont; + TextEncoding encoding = GetApplicationTextEncoding(); + CFStringRef cfStr; + ThemeDrawState drawState; + Rect bounds = {y, x + menuMarkIndent, y + height, x + width}; + + if (mark < kSpaceCharCode) { + font = kThemeMenuItemCmdKeyFont; + encoding = kTextEncodingMacKeyboardGlyphs; + } + switch (mePtr->state) { + case ENTRY_ACTIVE: + drawState = kThemeStatePressed; + break; + case ENTRY_DISABLED: + drawState = kThemeStateInactive; + break; + default: + drawState = kThemeStateActive; + break; + } + cfStr = CFStringCreateWithBytes(NULL, (UInt8*)&mark, 1, + encoding, false); + if (cfStr) { + DrawThemeText(d, gc, cfStr, font, drawState, &bounds, + baseline, teFlushDefault); + CFRelease(cfStr); + } + } else if (mark != 0) { + const char *markUtf = NULL; + int len; + + len = GetUtfMarkCharacter(mark, &markUtf); + Tk_DrawChars(menuPtr->display, d, gc, tkfont, markUtf, len, + x + menuMarkIndent, baseline); + } + } + } +} + +#ifdef USE_TK_MDEF +/* + *---------------------------------------------------------------------- + * + * DrawMenuBackground -- + * + * If Appearance is present, draws the Appearance background + * + * Results: + * Nothing + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuBackground( + TkMenu *menuPtr, + Rect *menuRectPtr, /* The menu rect */ + Drawable d) /* What we are drawing into */ +{ + Tk_3DBorder border; + + EraseMenuBackground(((MacMenu *) menuPtr->platformData)->menuHdl, + menuRectPtr, ((MacDrawable*)d)->context); + border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); + Tk_Fill3DRectangle(menuPtr->tkwin, d, border, + menuRectPtr->left, menuRectPtr->top, + menuRectPtr->right - menuRectPtr->left, + menuRectPtr->bottom - menuRectPtr->top, 0, TK_RELIEF_FLAT); +} +#endif /* USE_TK_MDEF */ + +/* + *---------------------------------------------------------------------- + * + * DrawMenuEntryAccelerator -- + * + * This procedure draws the accelerator part of a menu. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuEntryAccelerator( + TkMenu *menuPtr, /* The menu we are drawing */ + TkMenuEntry *mePtr, /* The entry we are drawing */ + Drawable d, /* The drawable we are drawing in */ + GC gc, /* The gc to draw into */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + Tk_3DBorder activeBorder, /* border for menu background */ + int x, /* The left side of the entry */ + int y, /* The top of the entry */ + int width, /* The width of the entry */ + int height, /* The height of the entry */ + int drawArrow) /* Whether or not to draw cascade arrow */ +{ + if (mePtr->type != CASCADE_ENTRY && mePtr->accelLength > 0) { + const char *accel = (mePtr->accelPtr == NULL) ? "" + : Tcl_GetString(mePtr->accelPtr); + EntryGeometry *geometryPtr = (EntryGeometry*)mePtr->platformEntryData; + int leftEdge = x + width - geometryPtr->accelTextWidth; + int baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2; + + if (IS_THEME_MENU_FONT(tkfont)) { + CFStringRef cfStr; + ThemeDrawState drawState; + + switch (mePtr->state) { + case ENTRY_ACTIVE: + drawState = kThemeStatePressed; + break; + case ENTRY_DISABLED: + drawState = kThemeStateInactive; + break; + default: + drawState = kThemeStateActive; + break; + } + if ((mePtr->entryFlags & ENTRY_ACCEL_MASK) == 0) { + leftEdge -= geometryPtr->modifierWidth; + } + if (geometryPtr->accelGlyph) { + Rect bounds = {y, leftEdge, y + height, leftEdge + + geometryPtr->accelTextWidth}; + + cfStr = CFStringCreateWithBytes(NULL, + (UInt8*)&geometryPtr->accelGlyph, 1, + kTextEncodingMacKeyboardGlyphs, false); + if (cfStr) { + DrawThemeText(d, gc, cfStr, kThemeMenuItemCmdKeyFont, + drawState, &bounds, baseline, teFlushDefault); + CFRelease(cfStr); + } + } else { + Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel + + geometryPtr->accelTextStart, mePtr->accelLength - + geometryPtr->accelTextStart, leftEdge, baseline); + } + if (geometryPtr->modifierNum) { + Rect bounds = {y, leftEdge - geometryPtr->modifierWidth, + y + height, leftEdge}; + + cfStr = CFStringCreateWithCharacters(NULL, + geometryPtr->modifierUniChars, + geometryPtr->modifierNum); + if (cfStr) { + DrawThemeText(d, gc, cfStr, kThemeMenuItemCmdKeyFont, + drawState, &bounds, baseline, teFlushDefault); + CFRelease(cfStr); + } + } + } else { + Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel, + mePtr->accelLength, leftEdge, baseline); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuSeparator -- + * + * The menu separator is drawn. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuSeparator( + 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 precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + int x, /* left coordinate of entry */ + int y, /* top coordinate of entry */ + int width, /* width of entry */ + int height) /* height of entry */ +{ + TkMacOSXDrawingContext dc; + Rect r; + + r.top = y; + r.left = x; + r.bottom = y + height; + r.right = x + width; + if (TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + ChkErr(DrawThemeMenuSeparator, &r); + TkMacOSXRestoreDrawingContext(&dc); + } +} + +#ifdef USE_TK_MDEF +/* + *---------------------------------------------------------------------- + * + * AppearanceEntryDrawWrapper -- + * + * It routes to the Appearance Managers DrawThemeEntry, which will then + * call us back after setting up the drawing context. + * + * Results: + * A menu entry is drawn + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ + +void +AppearanceEntryDrawWrapper( + TkMenuEntry *mePtr, + Rect *menuRectPtr, + MenuTrackingData *mtdPtr, + Drawable d, + Tk_FontMetrics *fmPtr, + Tk_Font tkfont, + int erase) +{ + MenuEntryUserData meData; + Rect itemRect; + ThemeMenuState theState; + ThemeMenuItemType theType; + Tk_FontMetrics entryMetrics; + + meData.mePtr = mePtr; + meData.mdefDrawable = d; + if (mePtr->fontPtr == NULL) { + meData.fmPtr = fmPtr; + meData.tkfont = tkfont; + } else { + meData.tkfont = Tk_GetFontFromObj(mePtr->menuPtr->tkwin, + mePtr->fontPtr); + Tk_GetFontMetrics(meData.tkfont, &entryMetrics); + fmPtr = &entryMetrics; + } + itemRect.left = menuRectPtr->left + mePtr->x; + itemRect.top = mtdPtr->virtualMenuTop + mePtr->y; + itemRect.right = mePtr->entryFlags & ENTRY_LAST_COLUMN ? + menuRectPtr->right : itemRect.left + mePtr->width; + itemRect.bottom = itemRect.top + mePtr->height; + + if (mePtr->state == ENTRY_ACTIVE) { + theState = kThemeMenuSelected; + } else if (mePtr->state == ENTRY_DISABLED) { + theState = kThemeMenuDisabled; + } else { + theState = kThemeMenuActive; + } + if (mePtr->type == CASCADE_ENTRY) { + theType = kThemeMenuItemHierarchical; + } else { + theType = kThemeMenuItemPlain; + } + if (erase) { + DisableScreenUpdates(); + DrawMenuBackground(mePtr->menuPtr, &itemRect, d); + } + DrawThemeMenuItem(menuRectPtr, &itemRect, + mtdPtr->virtualMenuTop, mtdPtr->virtualMenuBottom, theState, + theType | kThemeMenuItemNoBackground, tkThemeMenuItemDrawingUPP, + (unsigned long) &meData); + if (erase) { + EnableScreenUpdates(); + } +} + +/* + *---------------------------------------------------------------------- + * + * ThemeMenuItemDrawingProc -- + * + * This routine is called from the Appearance DrawThemeMenuEntry + * + * Results: + * A menu entry is drawn + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ + +pascal void +ThemeMenuItemDrawingProc( + const Rect *inBounds, + SInt16 inDepth, + Boolean inIsColorDevice, + SInt32 inUserData) +{ + MenuEntryUserData *meData = (MenuEntryUserData *) inUserData; + + TkpDrawMenuEntry(meData->mePtr, meData->mdefDrawable, meData->tkfont, + meData->fmPtr, inBounds->left, inBounds->top, inBounds->right - + inBounds->left + menuItemExtraWidth, inBounds->bottom - + inBounds->top + menuItemExtraHeight, 0, 1); +} +#endif /* USE_TK_MDEF */ + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXHandleTearoffMenu() -- + * + * This routine sees if the MDEF has set a menu and a mouse position for + * tearing off and makes a tearoff menu if it has. + * + * Results: + * menuPtr->interp will have the result of the tearoff command. + * + * Side effects: + * A new tearoff menu is created if it is supposed to be. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXHandleTearoffMenu(void) +{ + /* + * Obsolete: Nothing to do. + */ +} + +/* + *-------------------------------------------------------------- + * + * 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_Interp *interp, /* The interpreter to set. */ + Tk_BindingTable bindingTable) + /* The table to add to. */ +{ + /* + * Nothing to do. + */ +} + +/* + *-------------------------------------------------------------- + * + * 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. */ +{ + TkpComputeStandardMenuGeometry(menuPtr); +} + +/* + *---------------------------------------------------------------------- + * + * DrawTearoffEntry -- + * + * This procedure draws a tearoff entry. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawTearoffEntry( + 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, /* Left edge of entry. */ + int y, /* Top edge of entry. */ + int width, /* Width of entry. */ + int height) /* Height of entry. */ +{ + XPoint points[2]; + int margin, segmentWidth, maxX; + Tk_3DBorder border; + + if (menuPtr->menuType != MASTER_MENU ) { + return; + } + + margin = fmPtr->linespace/2; + points[0].x = x; + points[0].y = y + height/2; + points[1].y = points[0].y; + segmentWidth = 6; + maxX = x + menuPtr->totalWidth - 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; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXSetHelpMenuItemCount -- + * + * Has to be called after the first call to InsertMenu. Sets up the + * global variable for the number of items in the unmodified help menu. + * NB. Nobody uses this any more, since you can get the number of system + * help items from HMGetHelpMenu trivially. But it is in the stubs + * table... + * + * Results: + * None. + * + * Side effects: + * Nothing. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXSetHelpMenuItemCount(void) +{ + /* + * Obsolete: Nothing to do. + */ +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXMenuClick -- + * + * Prepares a menubar for MenuSelect or MenuKey. + * + * Results: + * None. + * + * Side effects: + * Any pending configurations of the menubar are completed. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXMenuClick(void) +{ + TkMenu *menuPtr; + TkMenuReferences *menuRefPtr; + + if ((currentMenuBarInterp != NULL) && (currentMenuBarName != NULL)) { + menuRefPtr = TkFindMenuReferences(currentMenuBarInterp, + currentMenuBarName); + for (menuPtr = menuRefPtr->menuPtr->masterMenuPtr; + menuPtr != NULL; menuPtr = menuPtr->nextInstancePtr) { + if (menuPtr->menuType == MENUBAR) { + CompleteIdlers(menuPtr); + break; + } + } + } + + if (menuBarFlags & MENUBAR_REDRAW_PENDING) { + Tcl_CancelIdleCall(DrawMenuBarWhenIdle, NULL); + DrawMenuBarWhenIdle(NULL); + } +} + +/* + *---------------------------------------------------------------------- + * + * 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. Only applies + * to Windows. */ +{ + GC gc; + TkMenu *menuPtr = mePtr->menuPtr; + int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0; + GC indicatorGC; + Tk_3DBorder bgBorder, activeBorder; + const Tk_FontMetrics *fmPtr; + Tk_FontMetrics entryMetrics; + int adjustedY = y + padY; + int adjustedHeight = height - 2 * padY; + + /* + * Choose the gc for drawing the foreground part of the entry. Under + * Appearance, we pass a null (appearanceGC) to tell ourselves not to + * change whatever color the appearance manager has set. + */ + + if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) { + gc = mePtr->activeGC; + if (gc == NULL) { + gc = menuPtr->activeGC; + } + } else { + TkMenuEntry *parentEntryPtr = GetParentMenuEntry(menuPtr); + + if (((parentEntryPtr && parentEntryPtr->state == ENTRY_DISABLED) || + (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; + } + + 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, x, adjustedY, width, adjustedHeight, drawArrow); + if (!mePtr->hideMargin) { + DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont, + fmPtr, x, adjustedY, width, adjustedHeight); + } + } +} + +/* + *-------------------------------------------------------------- + * + * 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, modifierWidth, labelWidth, indicatorSpace; + int windowWidth, windowHeight, accelWidth, maxAccelTextWidth; + int i, j, lastColumnBreak, maxModifierWidth, maxWidth, nonAccelMargin; + int maxNonAccelMargin, maxEntryWithAccelWidth, maxEntryWithoutAccelWidth; + int entryWidth, maxIndicatorSpace, borderWidth, activeBorderWidth; + TkMenuEntry *mePtr, *columnEntryPtr; + EntryGeometry *geometryPtr; + int haveAccel = 0; + + if (menuPtr->tkwin == NULL) { + return; + } + + Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, + &borderWidth); + Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, + &activeBorderWidth); + x = y = borderWidth; + indicatorSpace = labelWidth = accelWidth = maxAccelTextWidth = 0; + windowHeight = maxWidth = lastColumnBreak = 0; + maxModifierWidth = maxNonAccelMargin = 0; + maxEntryWithAccelWidth = maxEntryWithoutAccelWidth = 0; + maxIndicatorSpace = 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); + + for (i = 0; i < menuPtr->numEntries; i++) { + mePtr = menuPtr->entries[i]; + if (mePtr->type == CASCADE_ENTRY || mePtr->accelLength > 0) { + haveAccel = 1; + break; + } + } + + 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 (maxIndicatorSpace != 0) { + maxIndicatorSpace += 2; + } + for (j = lastColumnBreak; j < i; j++) { + columnEntryPtr = menuPtr->entries[j]; + geometryPtr = + (EntryGeometry *) columnEntryPtr->platformEntryData; + + columnEntryPtr->indicatorSpace = maxIndicatorSpace; + columnEntryPtr->width = maxIndicatorSpace + maxWidth + + 2 * activeBorderWidth; + geometryPtr->accelTextWidth = maxAccelTextWidth; + geometryPtr->modifierWidth = maxModifierWidth; + columnEntryPtr->x = x; + columnEntryPtr->entryFlags &= ~ENTRY_LAST_COLUMN; + if (maxEntryWithoutAccelWidth > maxEntryWithAccelWidth) { + geometryPtr->nonAccelMargin = maxEntryWithoutAccelWidth + - maxEntryWithAccelWidth; + if (geometryPtr->nonAccelMargin > maxNonAccelMargin) { + geometryPtr->nonAccelMargin = maxNonAccelMargin; + } + } else { + geometryPtr->nonAccelMargin = 0; + } + } + x += maxIndicatorSpace + maxWidth + 2 * borderWidth; + windowWidth = x; + maxWidth = maxIndicatorSpace = maxAccelTextWidth = 0; + maxModifierWidth = maxNonAccelMargin = maxEntryWithAccelWidth = 0; + maxEntryWithoutAccelWidth = 0; + lastColumnBreak = i; + y = borderWidth; + } + /*geometryPtr = (EntryGeometry *) mePtr->platformEntryData;*/ /* dead code */ + + if (mePtr->type == SEPARATOR_ENTRY) { + GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont, + fmPtr, &entryWidth, &height); + mePtr->height = height; + } else if (mePtr->type == TEAROFF_ENTRY) { + GetTearoffEntryGeometry(menuPtr, mePtr, tkfont, + fmPtr, &entryWidth, &height); + mePtr->height = height; + } 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, &labelWidth, &height); + mePtr->height = height; + + nonAccelMargin = 0; + if (mePtr->type == CASCADE_ENTRY) { + GetMenuAccelGeometry(menuPtr, mePtr, tkfont, fmPtr, + &modifierWidth, &accelWidth, &height); + } else if (mePtr->accelLength == 0) { + if (haveAccel && !mePtr->hideMargin) { + if (IS_THEME_MENU_FONT(tkfont)) { + nonAccelMargin = menuSymbols[COMMAND_SYMBOL].width; + } else { + nonAccelMargin = Tk_TextWidth(tkfont, + menuSymbols[COMMAND_SYMBOL].utf, + menuSymbols[COMMAND_SYMBOL].utfLen); + } + } + accelWidth = modifierWidth = 0; + } else { + GetMenuAccelGeometry(menuPtr, mePtr, tkfont, + fmPtr, &modifierWidth, &accelWidth, &height); + if (height > mePtr->height) { + mePtr->height = height; + } + } + + if (!(mePtr->hideMargin)) { + GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, + fmPtr, &indicatorSpace, &height); + if (height > mePtr->height) { + mePtr->height = height; + } + } else { + indicatorSpace = 0; + } + + if (nonAccelMargin > maxNonAccelMargin) { + maxNonAccelMargin = nonAccelMargin; + } + if (accelWidth > maxAccelTextWidth) { + maxAccelTextWidth = accelWidth; + } + if (modifierWidth > maxModifierWidth) { + maxModifierWidth = modifierWidth; + } + if (indicatorSpace > maxIndicatorSpace) { + maxIndicatorSpace = indicatorSpace; + } + + entryWidth = labelWidth + modifierWidth + accelWidth + + nonAccelMargin; + + if (entryWidth > maxWidth) { + maxWidth = entryWidth; + } + + if (mePtr->accelLength > 0) { + if (entryWidth > maxEntryWithAccelWidth) { + maxEntryWithAccelWidth = entryWidth; + } + } else { + if (entryWidth > maxEntryWithoutAccelWidth) { + maxEntryWithoutAccelWidth = entryWidth; + } + } + mePtr->height += 2 * activeBorderWidth; + } + mePtr->y = y; + y += menuPtr->entries[i]->height + borderWidth; + if (y > windowHeight) { + windowHeight = y; + } + } + + for (j = lastColumnBreak; j < menuPtr->numEntries; j++) { + columnEntryPtr = menuPtr->entries[j]; + geometryPtr = (EntryGeometry *) columnEntryPtr->platformEntryData; + + columnEntryPtr->indicatorSpace = maxIndicatorSpace; + columnEntryPtr->width = maxIndicatorSpace + maxWidth + + 2 * activeBorderWidth; + geometryPtr->accelTextWidth = maxAccelTextWidth; + columnEntryPtr->x = x; + columnEntryPtr->entryFlags |= ENTRY_LAST_COLUMN; + if (maxEntryWithoutAccelWidth > maxEntryWithAccelWidth) { + geometryPtr->nonAccelMargin = maxEntryWithoutAccelWidth + - maxEntryWithAccelWidth; + if (geometryPtr->nonAccelMargin > maxNonAccelMargin) { + geometryPtr->nonAccelMargin = maxNonAccelMargin; + } + } else { + geometryPtr->nonAccelMargin = 0; + } + } + windowWidth = x + maxIndicatorSpace + maxWidth + + 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; +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + *---------------------------------------------------------------------- + */ + +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, /* right edge */ + int width, /* width of entry */ + int height) /* height of entry */ +{ + int imageWidth, imageHeight, textWidth = 0, textHeight = 0; + int indicatorSpace = mePtr->indicatorSpace; + int leftEdge = x + indicatorSpace; + int haveImage = 0, haveText = 0; + int imageXOffset = 0, imageYOffset = 0; + int textXOffset = 0, textYOffset = 0; + Pixmap bitmap = (Pixmap) NULL; + Tcl_DString itemTextDString; + + /* + * 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) { + 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) { + GetEntryText(mePtr, &itemTextDString); + if (mePtr->compound != COMPOUND_NONE) { + textWidth = Tk_TextWidth(tkfont, + Tcl_DStringValue(&itemTextDString), + Tcl_DStringLength(&itemTextDString)) + + menuTextLeadingEdgeMargin + menuTextTrailingEdgeMargin; + textHeight = fmPtr->linespace; + } + haveText = 1; + } + } + + /* + * Now work out what the relative positions are. + */ + + if (haveImage && haveText && (mePtr->compound != COMPOUND_NONE)) { + 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 - menuTextLeadingEdgeMargin; + if ((mePtr->type != CHECK_BUTTON_ENTRY) + && (mePtr->type != RADIO_BUTTON_ENTRY)) { + textXOffset -= indicatorSpace; + imageXOffset = -indicatorSpace; + } + if (textXOffset < 0) { + textXOffset = 0; + } + break; + case COMPOUND_RIGHT: + imageXOffset = textWidth + 2 - menuTextTrailingEdgeMargin; + break; + case COMPOUND_CENTER: + textXOffset = (fullWidth - textWidth)/2; + imageXOffset = (fullWidth - imageWidth)/2; + break; + case COMPOUND_NONE: + /* + * Never reached. + */ + break; + } + } + + /* + * 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, + y + (mePtr->height - imageHeight)/2 + imageYOffset); + } else { + Tk_RedrawImage(mePtr->image, 0, 0, imageWidth, imageHeight, + d, leftEdge + imageXOffset, + y + (mePtr->height - imageHeight)/2 + imageYOffset); + } + } else if (mePtr->bitmapPtr != NULL) { + XCopyPlane(menuPtr->display, bitmap, d, gc, 0, 0, imageWidth, + imageHeight, leftEdge + imageXOffset, + y + (mePtr->height - imageHeight)/2 + imageYOffset, 1); + } + if (haveText) { + int baseline = y + (height + fmPtr->ascent - fmPtr->descent)/2; + + Tk_DrawChars(menuPtr->display, d, gc, tkfont, + Tcl_DStringValue(&itemTextDString), + Tcl_DStringLength(&itemTextDString), + leftEdge + menuTextLeadingEdgeMargin + textXOffset, + baseline + textYOffset); + Tcl_DStringFree(&itemTextDString); + } + + if (mePtr->state == ENTRY_DISABLED) { + if (menuPtr->disabledFgPtr == NULL) { + /* XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y, + width, height); */ + } else if ((mePtr->image != NULL) + && (menuPtr->disabledImageGC != None)) { + XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC, + leftEdge + imageXOffset, + y + (mePtr->height - imageHeight)/2 + imageYOffset, + imageWidth, imageHeight); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuEntryBackground -- + * + * This procedure draws the background part of a menu entry. Under + * Appearance, we only draw the background if the entry's border is set, + * we DO NOT inherit it from the menu... + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuEntryBackground( + TkMenu *menuPtr, /* The menu we are drawing. */ + TkMenuEntry *mePtr, /* The entry we are drawing. */ + Drawable d, /* What we are drawing into */ + Tk_3DBorder activeBorder, /* Border for active items */ + Tk_3DBorder bgBorder, /* Border for the background */ + int x, /* left edge */ + int y, /* top edge */ + int width, /* width of rectangle to draw */ + int height) /* height of rectangle to draw */ +{ + if ((menuPtr->menuType == TEAROFF_MENU) + || ((mePtr->state == ENTRY_ACTIVE) + && (mePtr->activeBorderPtr != None)) + || ((mePtr->state != ENTRY_ACTIVE) && (mePtr->borderPtr != None))) { + if (mePtr->state == ENTRY_ACTIVE) { + bgBorder = activeBorder; + } + Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, + x, y, width, height, 0, TK_RELIEF_FLAT); + } +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + *---------------------------------------------------------------------- + */ + +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, tornOff = (menuPtr->menuType == TEAROFF_MENU); +#ifdef USE_TK_MDEF + const int useMDEF = ((MacMenu *) menuPtr->platformData)->useMDEF; +#endif + + if (mePtr->image != NULL && (useMDEF || tornOff)) { + Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr); + haveImage = 1; + } else if (mePtr->bitmapPtr != NULL && (useMDEF || tornOff)) { + Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); + Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr); + haveImage = 1; + } + if (!haveImage || (mePtr->compound != COMPOUND_NONE)) { + int textWidth = 0, textHeight = fmPtr->linespace; + + if (mePtr->labelPtr != NULL) { + Tcl_DString itemTextDString; + + GetEntryText(mePtr, &itemTextDString); + textWidth = Tk_TextWidth(tkfont, + Tcl_DStringValue(&itemTextDString), + Tcl_DStringLength(&itemTextDString)) + + menuTextLeadingEdgeMargin + menuTextTrailingEdgeMargin; + Tcl_DStringFree(&itemTextDString); + + if (haveImage && (mePtr->compound != COMPOUND_NONE)) { + switch ((enum compound) mePtr->compound) { + int margin; + + case COMPOUND_TOP: + case COMPOUND_BOTTOM: + if (textWidth > *widthPtr) { + *widthPtr = textWidth; + } + *heightPtr += textHeight + 2; + break; + case COMPOUND_LEFT: + margin = *widthPtr + 2; + if (margin > menuTextLeadingEdgeMargin) { + margin = menuTextLeadingEdgeMargin; + } + *widthPtr += textWidth + 2 - margin; + if (textHeight > *heightPtr) { + *heightPtr = textHeight; + } + break; + case COMPOUND_RIGHT: + margin = menuTextTrailingEdgeMargin; + *widthPtr += textWidth + 2 - margin; + if (textHeight > *heightPtr) { + *heightPtr = textHeight; + } + break; + case COMPOUND_CENTER: + if (textWidth > *widthPtr) { + *widthPtr = textWidth; + } + if (textHeight > *heightPtr) { + *heightPtr = textHeight; + } + break; + case COMPOUND_NONE: + /* + * Never reached. + */ + break; + } + goto labelGeomDone; + } + } + *widthPtr = textWidth; + *heightPtr = textHeight; + } + + labelGeomDone: + *heightPtr += menuItemExtraHeight; + *widthPtr += menuItemExtraWidth; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGenerateParentMenuSelectEvent -- + * + * Respond to a hierarchical menu being opened. + * + * Results: + * True if event(s) are generated - false otherwise. + * + * Side effects: + * Places a virtual event on the event queue. + * + *---------------------------------------------------------------------- + */ + +int +TkMacOSXGenerateParentMenuSelectEvent( + MenuRef menu) +{ + TkMenu *menuPtr = MenuPtrForMenuRef(menu); + + if (menuPtr) { + TkMenuEntry *parentEntryPtr = GetParentMenuEntry(menuPtr); + + if (parentEntryPtr && (menuPtr = parentEntryPtr->menuPtr)) { + TkActivateMenuEntry(menuPtr, parentEntryPtr->index); + MenuSelectEvent(menuPtr); + Tcl_ServiceAll(); + return true; + } + } + return false; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGenerateMenuSelectEvent -- + * + * Respond to a menu item being selected. + * + * Results: + * True if event(s) are generated - false otherwise. + * + * Side effects: + * Places a virtual event on the event queue. + * + *---------------------------------------------------------------------- + */ + +int +TkMacOSXGenerateMenuSelectEvent( + MenuRef menu, + MenuItemIndex index) +{ + TkMenu *menuPtr = MenuPtrForMenuRef(menu); + int item = index - 1; + + if (menuPtr) { + if (item < 0 || item >= menuPtr->numEntries || + (menuPtr->entries[item])->state == ENTRY_DISABLED) { + TkActivateMenuEntry(menuPtr, -1); + } else { + TkActivateMenuEntry(menuPtr, item); + MenuSelectEvent(menuPtr); + Tcl_ServiceAll(); + return true; + } + } + return false; +} + +/* + *---------------------------------------------------------------------- + * + * MenuSelectEvent -- + * + * Generates a "MenuSelect" virtual event. This can be used to do + * context-sensitive menu help. + * + * Results: + * None. + * + * Side effects: + * Places a virtual event on the event queue. + * + *---------------------------------------------------------------------- + */ + +void +MenuSelectEvent( + TkMenu *menuPtr) /* the menu we have selected. */ +{ + XVirtualEvent event; + + bzero(&event, sizeof(XVirtualEvent)); + event.type = VirtualEvent; + event.serial = menuPtr->display->request; + event.send_event = false; + event.display = menuPtr->display; + Tk_MakeWindowExist(menuPtr->tkwin); + event.event = Tk_WindowId(menuPtr->tkwin); + event.root = XRootWindow(menuPtr->display, 0); + event.subwindow = None; + event.time = TkpGetMS(); + + XQueryPointer(NULL, None, NULL, NULL, &event.x_root, &event.y_root, NULL, + NULL, &event.state); + event.same_screen = true; + event.name = Tk_GetUid("MenuSelect"); + Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXClearActiveMenu -- + * + * Clears Tk's active entry for the given MenuRef. + * + * Results: + * None. + * + * Side effects: + * Generates <<MenuSelect>> virtual events. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXClearActiveMenu( + MenuRef menu) +{ + TkMenu *menuPtr = MenuPtrForMenuRef(menu); + + if (menuPtr) { + RecursivelyClearActiveMenu(menuPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * RecursivelyClearActiveMenu -- + * + * Recursively clears the active entry in the menu's cascade hierarchy. + * + * Results: + * None. + * + * Side effects: + * Generates <<MenuSelect>> virtual events. + * + *---------------------------------------------------------------------- + */ + +void +RecursivelyClearActiveMenu( + TkMenu *menuPtr) /* The menu to reset. */ +{ + int i; + TkMenuEntry *mePtr; + + TkActivateMenuEntry(menuPtr, -1); + for (i = 0; i < menuPtr->numEntries; i++) { + mePtr = menuPtr->entries[i]; + if (mePtr->type == CASCADE_ENTRY) { + if ((mePtr->childMenuRefPtr != NULL) + && (mePtr->childMenuRefPtr->menuPtr != NULL)) { + RecursivelyClearActiveMenu(mePtr->childMenuRefPtr->menuPtr); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXClearMenubarActive -- + * + * Recursively clears the active entry in the current menubar hierarchy. + * + * Results: + * None. + * + * Side effects: + * Generates <<MenuSelect>> virtual events. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXClearMenubarActive(void) +{ + TkMenuReferences *menuBarRefPtr; + + if (currentMenuBarName != NULL) { + menuBarRefPtr = TkFindMenuReferences(currentMenuBarInterp, + currentMenuBarName); + if ((menuBarRefPtr != NULL) && (menuBarRefPtr->menuPtr != NULL)) { + TkMenu *menuPtr; + + for (menuPtr = menuBarRefPtr->menuPtr->masterMenuPtr; + menuPtr != NULL; menuPtr = menuPtr->nextInstancePtr) { + if (menuPtr->menuType == MENUBAR) { + RecursivelyClearActiveMenu(menuPtr); + } + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * 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. Only applicable to Windows. + * + * Results: + * None. + * + * Side effects: + * An idle handler is set up to do the reconfiguration. + * + *---------------------------------------------------------------------- + */ + +void +TkpMenuNotifyToplevelCreate( + Tcl_Interp *interp, /* The interp the menu lives in. */ + const char *menuName) /* The name of the menu to reconfigure. */ +{ + /* + * Nothing to do. + */ +} + +/* + *---------------------------------------------------------------------- + * + * TkpMenuInit -- + * + * Initializes Mac-specific menu data. + * + * Results: + * None. + * + * Side effects: + * Allocates a hash table. + * + *---------------------------------------------------------------------- + */ + +void +TkpMenuInit(void) +{ + MenuSymbol *ms = menuSymbols; + CFStringRef cfStr; + + lastMenuID = 256; + Tcl_InitHashTable(&commandTable, TCL_ONE_WORD_KEYS); + currentMenuBarOwner = NULL; + currentAppleMenuID = 0; + currentHelpMenuID = 0; + currentMenuBarInterp = NULL; + currentMenuBarName = NULL; + windowListPtr = NULL; + +#ifdef USE_TK_MDEF + tkThemeMenuItemDrawingUPP + = NewMenuItemDrawingUPP(ThemeMenuItemDrawingProc); + useMDEFVar = Tcl_NewStringObj("::tk::mac::useCustomMDEF", -1); + macMDEFDrawable.winPtr = NULL; + macMDEFDrawable.xOff = 0; + macMDEFDrawable.yOff = 0; + macMDEFDrawable.visRgn = NULL; + macMDEFDrawable.aboveVisRgn = NULL; + macMDEFDrawable.drawRect = CGRectNull; + macMDEFDrawable.referenceCount = 0; + macMDEFDrawable.toplevel = NULL; + macMDEFDrawable.flags = 0; + macMDEFDrawable.grafPtr = NULL; + macMDEFDrawable.context = NULL; + macMDEFDrawable.size = CGSizeZero; +#endif + + ChkErr(GetThemeMetric, kThemeMetricMenuMarkColumnWidth, + &menuMarkColumnWidth); + ChkErr(GetThemeMetric, kThemeMetricMenuMarkIndent, &menuMarkIndent); + ChkErr(GetThemeMetric, kThemeMetricMenuTextLeadingEdgeMargin, + &menuTextLeadingEdgeMargin); + ChkErr(GetThemeMetric, kThemeMetricMenuTextTrailingEdgeMargin, + &menuTextTrailingEdgeMargin); + ChkErr(GetThemeMenuItemExtra, kThemeMenuItemPlain, &menuItemExtraHeight, + &menuItemExtraWidth); + ChkErr(GetThemeMenuSeparatorHeight, &menuSeparatorHeight); + + while (ms->unicode) { + ms->utfLen = Tcl_UniCharToUtf(ms->unicode, ms->utf); + ms->utf[ms->utfLen] = 0; + cfStr = CFStringCreateWithCharacters(NULL, &ms->unicode, 1); + if (cfStr) { + ms->width = MeasureThemeText(cfStr, kThemeMenuItemCmdKeyFont); + CFRelease(cfStr); + } + ms++; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpMenuThreadInit -- + * + * Does platform-specific initialization of thread-specific menu state. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpMenuThreadInit(void) +{ + /* + * Nothing to do. + */ +} + +/* + *---------------------------------------------------------------------- + * + * TkpPreprocessMacMenu -- + * + * Handle preprocessing of menubar if it exists. + * + * Results: + * None. + * + * Side effects: + * All post commands for the current menubar get executed. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXPreprocessMenu(void) +{ + if ((currentMenuBarName != NULL) && (currentMenuBarInterp != NULL)) { + TkMenuReferences *mbRefPtr = + TkFindMenuReferences(currentMenuBarInterp,currentMenuBarName); + + if ((mbRefPtr != NULL) && (mbRefPtr->menuPtr != NULL)) { + int code; + + Tcl_Preserve(currentMenuBarInterp); + code = TkPreprocessMenu(mbRefPtr->menuPtr->masterMenuPtr); + if ((code != TCL_OK) && (code != TCL_CONTINUE) + && (code != TCL_BREAK)) { + Tcl_AddErrorInfo(currentMenuBarInterp, + "\n (menu preprocess)"); + Tcl_BackgroundException(currentMenuBarInterp, code); + } + Tcl_Release(currentMenuBarInterp); + } + } +} + +#ifdef USE_TK_MDEF +#pragma mark MDEF +/* + *---------------------------------------------------------------------- + * + * MenuDefProc -- + * + * This routine is the MDEF handler for Tk. It receives all messages for + * the menu and dispatches them. + * + * Results: + * None. + * + * Side effects: + * This routine causes menus to be drawn and will certainly allocate + * memory as a result. Also, the menu can scroll up and down, and various + * other interface actions can take place. + * + *---------------------------------------------------------------------- + */ + +void +MenuDefProc( + SInt16 message, /* What action are we taking? */ + MenuRef menu, /* The menu we are working with */ + Rect *menuRectPtr, /* A pointer to the rect for the whole + * menu. */ + Point hitPt, /* Where the mouse was clicked for the + * appropriate messages. */ + SInt16 *whichItem) /* Output result. Which item was hit by the + * user? */ +{ + TkMenu *menuPtr; + Tcl_HashEntry *commandEntryPtr; + MenuID menuID; + + menuID = GetMenuID(menu); + commandEntryPtr = Tcl_FindHashEntry(&commandTable, (char*)(intptr_t)menuID); + if (!commandEntryPtr) return; + menuPtr = Tcl_GetHashValue(commandEntryPtr); + + switch (message) { + case kMenuInitMsg: + *whichItem = noErr; + break; + case kMenuDisposeMsg: + break; + case kMenuHiliteItemMsg: + HandleMenuHiliteMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr); + break; + case kMenuCalcItemMsg: + HandleMenuCalcItemMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr); + break; + case kMenuDrawItemsMsg: +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("MDEF: DrawItemsMsg"); +#endif + /* + * We do nothing here, because we don't support the Menu Managers + * dynamic item groups + */ + + break; + case kMenuThemeSavvyMsg: + *whichItem = kThemeSavvyMenuResponse; + break; + case kMenuSizeMsg: +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("MDEF: SizeMsg %d, %d", hitPt.h, hitPt.v); +#endif + SetMenuWidth(menu, hitPt.h < menuPtr->totalWidth ? hitPt.h : + menuPtr->totalWidth); + SetMenuHeight(menu, hitPt.v < menuPtr->totalHeight ? hitPt.v : + menuPtr->totalHeight); + break; + case kMenuDrawMsg: + HandleMenuDrawMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr); + break; + case kMenuFindItemMsg: + HandleMenuFindItemMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr); + break; + case kMenuPopUpMsg: + HandleMenuPopUpMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr); + break; + } +} + +/* + *---------------------------------------------------------------------- + * + * HandleMenuHiliteMsg -- + * + * Handles the MenuDefProc's hilite message. + * + * Results: + * A menu entry is drawn + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ + +void +HandleMenuHiliteMsg( + MenuRef menu, + Rect *menuRectPtr, + Point hitPt, + SInt16 *whichItem, + TkMenu *menuPtr) +{ + OSStatus err; + Tk_Font tkfont; + Tk_FontMetrics fontMetrics; + MDEFHiliteItemData *hidPtr = (MDEFHiliteItemData *) whichItem; + int oldItem = hidPtr->previousItem - 1; + int newItem = hidPtr->newItem - 1; + MenuTrackingData mtd, *mtdPtr = &mtd; + +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("MDEF: HiliteMsg %d -> %d", hidPtr->previousItem, + hidPtr->newItem); +#endif + GetPort(&macMDEFDrawable.grafPtr); + macMDEFDrawable.context = (CGContextRef) hidPtr->context; + + err = ChkErr(GetMenuTrackingData, menu, mtdPtr); + if (err != noErr) { + return; + } + + tkfont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); + Tk_GetFontMetrics(tkfont, &fontMetrics); + if (oldItem >= 0) { + AppearanceEntryDrawWrapper(menuPtr->entries[oldItem], menuRectPtr, + mtdPtr, (Drawable) &macMDEFDrawable, &fontMetrics, tkfont, 1); + } + if (newItem >= 0) { + AppearanceEntryDrawWrapper(menuPtr->entries[newItem], menuRectPtr, + mtdPtr, (Drawable) &macMDEFDrawable, &fontMetrics, tkfont, 0); + } +} + +/* + *---------------------------------------------------------------------- + * + * HandleMenuDrawMsg -- + * + * Handles the MenuDefProc's draw message. + * + * Results: + * A menu entry is drawn + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ + +void +HandleMenuDrawMsg( + MenuRef menu, + Rect *menuRectPtr, + Point hitPt, + SInt16 *whichItem, + TkMenu *menuPtr) +{ + Tk_Font menuFont; + Tk_FontMetrics fontMetrics; + TkMenuEntry *mePtr; + int i; + Rect menuClipRect, bounds; + MDEFDrawData *ddPtr = (MDEFDrawData *) whichItem; + MenuTrackingData *mtdPtr = &(ddPtr->trackingData); + TkWindow *winPtr = (TkWindow *) menuPtr->tkwin; + + GetPort(&macMDEFDrawable.grafPtr); + GetPortBounds(macMDEFDrawable.grafPtr, &bounds); + macMDEFDrawable.context = (CGContextRef) ddPtr->context; +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("MDEF: DrawMsg %d - %d; %d - %d", menuRectPtr->top, + menuRectPtr->bottom, bounds.top, bounds.bottom); +#endif + winPtr->changes.x = menuRectPtr->left; + winPtr->changes.y = menuRectPtr->top; + winPtr->changes.width = menuRectPtr->right - menuRectPtr->left; + winPtr->changes.height = menuRectPtr->bottom - menuRectPtr->top; + TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, + 0, 0, -1, -1); +#if 0 + if (menuPtr->menuRefPtr->topLevelListPtr != NULL) { + menuType = kThemeMenuTypePullDown; + } else if (menuPtr->menuRefPtr->parentEntryPtr != NULL) { + menuType = kThemeMenuTypeHierarchical; + } else { + menuType = kThemeMenuTypePopUp; + } +#endif + DrawMenuBackground(menuPtr, menuRectPtr, (Drawable) &macMDEFDrawable); + menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); + Tk_GetFontMetrics(menuFont, &fontMetrics); + menuClipRect = *menuRectPtr; + mtdPtr->virtualMenuBottom = mtdPtr->virtualMenuTop + menuPtr->totalHeight; + + /* + * Next, figure out scrolling information. + */ + + if ((menuRectPtr->bottom - menuRectPtr->top) < menuPtr->totalHeight) { + short arrowHeight = fontMetrics.linespace + 1; + Rect arrowRect, eraseRect; + ThemeMenuState menuState = IsMenuItemEnabled(menu, 0) ? + kThemeMenuActive : kThemeMenuDisabled; + + if (mtdPtr->virtualMenuTop < menuRectPtr->top) { + arrowRect = bounds; + /*arrowRect.top += 1;*/ + arrowRect.bottom = arrowRect.top + arrowHeight; + eraseRect = arrowRect; + eraseRect.top = menuRectPtr->top; + menuClipRect.top = arrowRect.bottom; + ChkErr(EraseMenuBackground, menu, &eraseRect, + macMDEFDrawable.context); + ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect, + mtdPtr->virtualMenuTop, mtdPtr->virtualMenuBottom, + menuState, kThemeMenuItemScrollUpArrow, NULL, 0); +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("upArrow: %d - %d, %d - %d", arrowRect.top, + arrowRect.bottom, arrowRect.left, arrowRect.right); +#endif + } + if (mtdPtr->virtualMenuBottom > menuRectPtr->bottom) { + arrowRect = bounds; + arrowRect.bottom -= 1; + arrowRect.top = arrowRect.bottom - arrowHeight; + eraseRect = arrowRect; + eraseRect.bottom = menuRectPtr->bottom; + menuClipRect.bottom = arrowRect.top; + ChkErr(EraseMenuBackground, menu, &eraseRect, + macMDEFDrawable.context); + ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect, + mtdPtr->virtualMenuTop, mtdPtr->virtualMenuBottom, + menuState, kThemeMenuItemScrollDownArrow, NULL, 0); +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("downArrow: %d - %d, %d - %d", arrowRect.top, + arrowRect.bottom, arrowRect.left, arrowRect.right); +#endif + } + TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, + menuClipRect.left, menuClipRect.top, menuClipRect.right - + menuClipRect.left, menuClipRect.bottom - menuClipRect.top); + } + + /* + * Now, actually draw the menu. Don't draw entries that are higher than + * the top arrow, and don't draw entries that are lower than the bottom. + */ + + for (i = 0; i < menuPtr->numEntries; i++) { + mePtr = menuPtr->entries[i]; + if (mtdPtr->virtualMenuTop + mePtr->y + mePtr->height < + menuClipRect.top || mtdPtr->virtualMenuTop + mePtr->y > + menuClipRect.bottom) { + continue; + } + AppearanceEntryDrawWrapper(mePtr, menuRectPtr, mtdPtr, + (Drawable) &macMDEFDrawable, &fontMetrics, menuFont, 0); + } + MDEFScrollFlag = 1; +} + +/* + *---------------------------------------------------------------------- + * + * HandleMenuFindItemMsg -- + * + * Handles the MenuDefProc's FindItems message. We have to respond by + * filling in the itemSelected, itemUnderMouse and itemRect fields. This + * is also the time to scroll the menu if it is too long to fit on the + * screen. + * + * Results: + * The Menu system is informed of the selected item & the item under the + * mouse. + * + * Side effects: + * The menu might get scrolled. + * + *---------------------------------------------------------------------- + */ + +void +HandleMenuFindItemMsg( + MenuRef menu, + Rect *menuRectPtr, + Point hitPt, + SInt16 *whichItem, + TkMenu *menuPtr) +{ + Tk_Font menuFont; + Tk_FontMetrics fontMetrics; + TkMenuEntry *mePtr; + int i, newItem = -1, itemUnderMouse = -1; + Rect itemRect = {0, 0, 0, 0}, menuClipRect, bounds; + int hasTopScroll, hasBottomScroll; + MDEFFindItemData *fiPtr = (MDEFFindItemData *)whichItem; + MenuTrackingData *mtdPtr = &(fiPtr->trackingData), topMtd; + enum { + DONT_SCROLL, DOWN_SCROLL, UP_SCROLL + } scrollDirection; + short arrowHeight; + +#ifdef TK_MAC_DEBUG_MENUS + static Point lastHitPt = {0, 0}; + + if (hitPt.h != lastHitPt.h || hitPt.v != lastHitPt.v) { + lastHitPt = hitPt; + TkMacOSXDbgMsg("MDEF: FindItemMsg: %d, %d", hitPt.h, hitPt.v); + } +#endif + + GetPort(&macMDEFDrawable.grafPtr); + GetPortBounds(macMDEFDrawable.grafPtr, &bounds); + macMDEFDrawable.context = (CGContextRef) fiPtr->context; + + /* + * Now we need to take care of scrolling the menu. + */ + + menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); + Tk_GetFontMetrics(menuFont, &fontMetrics); + arrowHeight = fontMetrics.linespace + 1; + menuClipRect = *menuRectPtr; + hasTopScroll = mtdPtr->virtualMenuTop < menuRectPtr->top; + hasBottomScroll = mtdPtr->virtualMenuBottom > menuRectPtr->bottom; + scrollDirection = DONT_SCROLL; + if (hasTopScroll) { + menuClipRect.top = bounds.top + arrowHeight; + if (hitPt.v < menuClipRect.top) { + newItem = -1; + scrollDirection = DOWN_SCROLL; + } + } + if (hasBottomScroll) { + menuClipRect.bottom = bounds.bottom - 1 - arrowHeight; + if (hitPt.v > menuClipRect.bottom) { + newItem = -1; + scrollDirection = UP_SCROLL; + } + } + if (MDEFScrollFlag) { + scrollDirection = DONT_SCROLL; + MDEFScrollFlag = 0; + } + /* + * Don't scroll if there are other menus open above us + */ + ChkErr(GetMenuTrackingData, NULL, &topMtd); + if (menu != topMtd.menu) { + scrollDirection = DONT_SCROLL; + } + if (scrollDirection == DONT_SCROLL) { + /* + * Find out which item was hit. If it is the same as the old item, we + * don't need to do anything. + */ + + if (PtInRect(hitPt, menuRectPtr)) { + for (i = 0; i < menuPtr->numEntries; i++) { + mePtr = menuPtr->entries[i]; + itemRect.left = menuRectPtr->left + mePtr->x; + itemRect.top = mtdPtr->virtualMenuTop + mePtr->y; + itemRect.right = mePtr->entryFlags & ENTRY_LAST_COLUMN ? + menuRectPtr->right : itemRect.left + mePtr->width; + itemRect.bottom = itemRect.top + mePtr->height; + if (PtInRect(hitPt, &itemRect)) { + if ((mePtr->type == SEPARATOR_ENTRY) + || (mePtr->state == ENTRY_DISABLED)) { + newItem = -1; + itemUnderMouse = i; + } else { + TkMenuEntry *parentEntryPtr = + GetParentMenuEntry(menuPtr); + + if (parentEntryPtr && + parentEntryPtr->state == ENTRY_DISABLED) { + newItem = -1; + itemUnderMouse = i; + } else { + newItem = i; + itemUnderMouse = i; + } + } + break; + } + } + } + } else { + short scrollAmt; + unsigned long scrollDelay; + Rect arrowRect, eraseRect, scrolledMenuClipRect; + ThemeMenuState menuState = IsMenuItemEnabled(menu, 0) ? + kThemeMenuActive : kThemeMenuDisabled; + int oldItem = mtdPtr->itemSelected - 1; + short d; + + TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, + 0, 0, -1, -1); + scrollAmt = fontMetrics.linespace + menuItemExtraHeight; + if (scrollDirection == UP_SCROLL) { + scrollAmt = -scrollAmt; + d = hitPt.v - bounds.bottom; + } else { + d = bounds.top - hitPt.v; + } + scrollDelay = (d >= scrollAmt/2) ? 1 : 10; + menuClipRect = *menuRectPtr; + if (mtdPtr->virtualMenuTop + scrollAmt < menuRectPtr->top) { + arrowRect = bounds; + /*arrowRect.top += 1;*/ + arrowRect.bottom = arrowRect.top + arrowHeight; + eraseRect = arrowRect; + eraseRect.top = menuRectPtr->top; + menuClipRect.top = arrowRect.bottom; + if (!hasTopScroll) { + ChkErr(EraseMenuBackground, menu, &eraseRect, + macMDEFDrawable.context); + ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect, + mtdPtr->virtualMenuTop + scrollAmt, + mtdPtr->virtualMenuBottom + scrollAmt, + menuState, kThemeMenuItemScrollUpArrow, NULL, 0); +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("upArrow: %d - %d, %d - %d", arrowRect.top, + arrowRect.bottom, arrowRect.left, arrowRect.right); +#endif + } + } + if (mtdPtr->virtualMenuBottom + scrollAmt > menuRectPtr->bottom) { + arrowRect = bounds; + arrowRect.bottom -= 1; + arrowRect.top = arrowRect.bottom - arrowHeight; + eraseRect = arrowRect; + eraseRect.bottom = menuRectPtr->bottom; + menuClipRect.bottom = arrowRect.top; + if (!hasBottomScroll) { + ChkErr(EraseMenuBackground, menu, &eraseRect, + macMDEFDrawable.context); + ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect, + mtdPtr->virtualMenuTop + scrollAmt, + mtdPtr->virtualMenuBottom + scrollAmt, + menuState, kThemeMenuItemScrollDownArrow, NULL, 0); +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("downArrow: %d - %d, %d - %d", arrowRect.top, + arrowRect.bottom, arrowRect.left, arrowRect.right); +#endif + } + } + TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, + menuClipRect.left, menuClipRect.top, menuClipRect.right - + menuClipRect.left, menuClipRect.bottom - menuClipRect.top); + TkActivateMenuEntry(menuPtr, -1); + if (oldItem >= 0) { + AppearanceEntryDrawWrapper(menuPtr->entries[oldItem], menuRectPtr, + mtdPtr, (Drawable) &macMDEFDrawable, &fontMetrics, + menuFont, 1); + } + ChkErr(ScrollMenuImage, menu, &menuClipRect, 0, scrollAmt, + macMDEFDrawable.context); + mtdPtr->virtualMenuTop += scrollAmt; + mtdPtr->virtualMenuBottom += scrollAmt; + scrolledMenuClipRect = menuClipRect; + OffsetRect(&scrolledMenuClipRect, 0, scrollAmt); + menuClipRect = bounds; + if (mtdPtr->virtualMenuTop < menuRectPtr->top) { + menuClipRect.top += arrowHeight; + } + if (mtdPtr->virtualMenuBottom > menuRectPtr->bottom) { + menuClipRect.bottom -= arrowHeight; + } + TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, + menuClipRect.left, menuClipRect.top, menuClipRect.right - + menuClipRect.left, menuClipRect.bottom - menuClipRect.top); + if (scrolledMenuClipRect.bottom < menuClipRect.bottom) { + menuClipRect.top = scrolledMenuClipRect.bottom; + } else if (scrolledMenuClipRect.top < menuClipRect.top) { + menuClipRect.bottom = scrolledMenuClipRect.top; + } + for (i = 0; i < menuPtr->numEntries; i++) { + mePtr = menuPtr->entries[i]; + if (mtdPtr->virtualMenuTop + mePtr->y + mePtr->height < + menuClipRect.top || mtdPtr->virtualMenuTop + mePtr->y > + menuClipRect.bottom) { + continue; + } +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("Drawing item %i", i); +#endif + AppearanceEntryDrawWrapper(mePtr, menuRectPtr, mtdPtr, + (Drawable) &macMDEFDrawable, &fontMetrics, menuFont, 1); + } + Delay(scrollDelay, NULL); + } + mtdPtr->itemSelected = newItem + 1; + mtdPtr->itemUnderMouse = itemUnderMouse + 1; + mtdPtr->itemRect = itemRect; +} + +/* + *---------------------------------------------------------------------- + * + * HandleMenuPopUpMsg -- + * + * Handles the MenuDefProc's PopUp message. The menu is posted with the + * selected item at the point given in hitPt. + * + * Results: + * A menu is posted. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +HandleMenuPopUpMsg( + MenuRef menu, + Rect *menuRectPtr, + Point hitPt, + SInt16 *whichItem, + TkMenu *menuPtr) +{ + int maxMenuHeight; + int oldItem; + Rect portRect; + BitMap screenBits; + static SInt16 menuBarHeight = 0; + +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("MDEF: PopUpMsg"); +#endif + + if (!menuBarHeight) { + ChkErr(GetThemeMenuBarHeight, &menuBarHeight); + } + GetQDGlobalsScreenBits(&screenBits); + + /* + * Note that for some oddball reason, h and v are reversed in the point + * given to us by the MDEF. + */ + + oldItem = *whichItem; + if (oldItem >= menuPtr->numEntries) { + oldItem = -1; + } + portRect.top = 0; + portRect.bottom = 1280; + maxMenuHeight = screenBits.bounds.bottom - screenBits.bounds.top + - menuBarHeight - SCREEN_MARGIN; + if (menuPtr->totalHeight > maxMenuHeight) { + menuRectPtr->top = menuBarHeight; + } else { + int delta; + + menuRectPtr->top = hitPt.h; + if (oldItem >= 0) { + menuRectPtr->top -= menuPtr->entries[oldItem]->y; + } + + if (menuRectPtr->top < menuBarHeight) { + /* + * Displace downward if the menu would stick off the top of the + * screen. + */ + + menuRectPtr->top = menuBarHeight + SCREEN_MARGIN; + } else { + /* + * Or upward if the menu sticks off the bottom end... + */ + + delta = menuRectPtr->top + menuPtr->totalHeight - maxMenuHeight; + if (delta > 0) { + menuRectPtr->top -= delta; + } + } + } + menuRectPtr->left = hitPt.v; + menuRectPtr->right = menuRectPtr->left + menuPtr->totalWidth; + menuRectPtr->bottom = menuRectPtr->top + + ((maxMenuHeight < menuPtr->totalHeight) + ? maxMenuHeight : menuPtr->totalHeight); + if (menuRectPtr->top == menuBarHeight) { + *whichItem = hitPt.h; + } else { + *whichItem = menuRectPtr->top; + } +} + +/* + *---------------------------------------------------------------------- + * + * HandleMenuCalcItemMsg -- + * + * Handles the MenuDefProc's CalcItem message. It is supposed to + * calculate the Rect of the menu entry in whichItem in the menu, and put + * that in menuRectPtr. I assume this works, but I have never seen the + * MenuManager send this message. + * + * Results: + * The Menu Manager is informed of the bounding rect of a menu rect. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +HandleMenuCalcItemMsg( + MenuRef menu, + Rect *menuRectPtr, + Point hitPt, + SInt16 *whichItem, + TkMenu *menuPtr) +{ + TkMenuEntry *mePtr; + MenuTrackingData mtd, *mtdPtr = &mtd; + OSStatus err; + int virtualTop, item = *whichItem-1; + + err = ChkErr(GetMenuTrackingData, menu, mtdPtr); + if (err == noErr) { + virtualTop = mtdPtr->virtualMenuTop; + } else { + virtualTop = 0; + } + + if (item >= 0 && item < menuPtr->numEntries) { + mePtr = menuPtr->entries[item]; + menuRectPtr->left = mePtr->x; + menuRectPtr->top = mePtr->y + virtualTop; + if (mePtr->entryFlags & ENTRY_LAST_COLUMN) { + menuRectPtr->right = menuPtr->totalWidth; + } else { + menuRectPtr->right = mePtr->x + mePtr->width; + } + menuRectPtr->bottom = menuRectPtr->top + mePtr->height; + } +#ifdef TK_MAC_DEBUG_MENUS + TkMacOSXDbgMsg("MDEF: CalcItemMsg %d: %d, %d", *whichItem, + menuRectPtr->left, menuRectPtr->top); +#endif +} +#endif /* USE_TK_MDEF */ + +/* + * Local Variables: + * fill-column: 78 + * c-basic-offset: 4 + * End: + */ |