summaryrefslogtreecommitdiffstats
path: root/carbon/tkMacOSXMenu.c
diff options
context:
space:
mode:
Diffstat (limited to 'carbon/tkMacOSXMenu.c')
-rw-r--r--carbon/tkMacOSXMenu.c4745
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:
+ */