tkMacOSXMenu.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:129k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tkMacOSXMenu.c --
  3.  *
  4.  * This module implements the Mac-platform specific features of menus.
  5.  *
  6.  * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
  7.  * Copyright 2001, Apple Computer, Inc.
  8.  * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net>
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * RCS: @(#) $Id: tkMacOSXMenu.c,v 1.6.2.30 2007/11/09 06:26:56 das Exp $
  14.  */
  15. #include "tkMacOSXPrivate.h"
  16. #include "tkMenubutton.h"
  17. #include "tkMenu.h"
  18. #include "tkColor.h"
  19. #include "tkFont.h"
  20. #include "tkMacOSXDebug.h"
  21. /*
  22. #ifdef TK_MAC_DEBUG
  23. #define TK_MAC_DEBUG_MENUS
  24. #endif
  25. */
  26. #define USE_TK_MDEF
  27. typedef struct MacMenu {
  28.     MenuRef menuHdl; /* The Menu Manager data structure. */
  29. #ifdef USE_TK_MDEF
  30.     int useMDEF; /* true if this menu uses the MDEF */
  31. #endif
  32. } MacMenu;
  33. typedef struct MenuEntryUserData {
  34.     Drawable mdefDrawable;
  35.     TkMenuEntry *mePtr;
  36.     Tk_Font tkfont;
  37.     Tk_FontMetrics *fmPtr;
  38. } MenuEntryUserData;
  39. /*
  40.  * Platform specific flags for menu entries
  41.  *
  42.  * ENTRY_COMMAND_ACCEL Indicates the entry has the command key
  43.  * in its accelerator string.
  44.  * ENTRY_OPTION_ACCEL Indicates the entry has the option key
  45.  * in its accelerator string.
  46.  * ENTRY_SHIFT_ACCEL Indicates the entry has the shift key
  47.  * in its accelerator string.
  48.  * ENTRY_CONTROL_ACCEL Indicates the entry has the control key
  49.  * in its accelerator string.
  50.  */
  51. #define ENTRY_COMMAND_ACCEL ENTRY_PLATFORM_FLAG1
  52. #define ENTRY_OPTION_ACCEL ENTRY_PLATFORM_FLAG2
  53. #define ENTRY_SHIFT_ACCEL ENTRY_PLATFORM_FLAG3
  54. #define ENTRY_CONTROL_ACCEL ENTRY_PLATFORM_FLAG4
  55. #define ENTRY_ACCEL_MASK (ENTRY_COMMAND_ACCEL | ENTRY_OPTION_ACCEL 
  56. | ENTRY_SHIFT_ACCEL | ENTRY_CONTROL_ACCEL)
  57. #define MODIFIER_NUM 4
  58. /*
  59.  * This structure is used to keep track of subfields within Macintosh menu
  60.  * items.
  61.  */
  62. typedef struct EntryGeometry {
  63.     int accelTextStart; /* Offset into the accel string where
  64.  * the text starts. Everything before
  65.  * this is modifier key descriptions.
  66.  */
  67.     int modifierWidth; /* Width of modifier symbols. */
  68.     int accelTextWidth; /* Width of the text after the modifier
  69.  * keys. */
  70.     int nonAccelMargin; /* The width of the margin for entries
  71.  * without accelerators. */
  72.     int modifierNum; /* Number of modifiers */
  73.     Tcl_UniChar modifierUniChars[MODIFIER_NUM];
  74. /* Modifiers in unicode */
  75.     char accelGlyph; /* Accelerator glyph, if any */
  76. } EntryGeometry;
  77. /*
  78.  * Structure to keep track of toplevel windows and their menubars.
  79.  */
  80. typedef struct TopLevelMenubarList {
  81.     struct TopLevelMenubarList *nextPtr;
  82. /* The next window in the list. */
  83.     Tk_Window tkwin; /* The toplevel window. */
  84.     TkMenu *menuPtr; /* The menu associated with this
  85.  * toplevel. */
  86. } TopLevelMenubarList;
  87. /*
  88.  * Platform-specific flags for menus.
  89.  *
  90.  * MENU_APPLE_MENU 0 indicates a custom Apple menu has
  91.  * not been installed; 1 a custom Apple
  92.  * menu has been installed.
  93.  * MENU_HELP_MENU 0 indicates a custom Help menu has
  94.  * not been installed; 1 a custom Help
  95.  * menu has been installed.
  96.  * MENU_RECONFIGURE_PENDING 1 indicates that an idle handler has
  97.  * been scheduled to reconfigure the
  98.  * Macintosh MenuHandle.
  99.  */
  100. #define MENU_APPLE_MENU MENU_PLATFORM_FLAG1
  101. #define MENU_HELP_MENU MENU_PLATFORM_FLAG2
  102. #define MENU_RECONFIGURE_PENDING MENU_PLATFORM_FLAG3
  103. #define CASCADE_CMD (0x1b) /* The special command char for cascade
  104.  * menus. */
  105. #define MENUBAR_REDRAW_PENDING 1
  106. static int gNoTkMenus = 0; /* This is used by Tk_MacOSXTurnOffMenus as the
  107.  * flag that Tk is not to draw any menus. */
  108. static Tcl_HashTable commandTable;
  109. /* The list of menuInstancePtrs associated with
  110.  * menu ids */
  111. static short currentAppleMenuID;
  112. /* The id of the current Apple menu. 0 for
  113.  * none. */
  114. static short currentHelpMenuID; /* The id of the current Help menu. 0 for
  115.  * none. */
  116. static Tcl_Interp *currentMenuBarInterp;
  117. /* The interpreter of the window that owns
  118.  * the current menubar. */
  119. static char *currentMenuBarName;
  120. /* Malloced. Name of current menu in menu bar.
  121.  * NULL if no menu set. TO DO: make this a
  122.  * DString. */
  123. static Tk_Window currentMenuBarOwner;
  124. /* Which window owns the current menu bar. */
  125. static int inPostMenu; /* We cannot be re-entrant like X
  126.  * windows. */
  127. static short lastMenuID; /* To pass to NewMenu; need to figure out
  128.  * a good way to do this. */
  129. static short lastCascadeID;
  130. /* Cascades have to have ids that are
  131.  * less than 256. */
  132. static int menuBarFlags; /* Used for whether the menu bar needs
  133.  * redrawing or not. */
  134. struct MenuCommandHandlerData { /* This is the ClientData we pass to */
  135.     TkMenu *menuPtr; /* Tcl_DoWhenIdle to move handling */
  136.     int index; /* menu commands to the event loop. */
  137. };
  138. static TopLevelMenubarList *windowListPtr;
  139. /* A list of windows that have menubars set. */
  140. /*
  141.  * Array of unicode, charcode and utf representations of the most common
  142.  * special menu symbols.
  143.  */
  144. typedef struct MenuSymbol {
  145.     const Tcl_UniChar unicode;
  146.     const char charCode;
  147.     /* char padding; */
  148.     int utfLen, width;
  149.     char utf[TCL_UTF_MAX + 1];
  150. } MenuSymbol;
  151. static MenuSymbol menuSymbols[] = {
  152.     {kCommandUnicode, kCommandCharCode},
  153.     {kOptionUnicode, kMenuOptionGlyph},
  154.     {kControlUnicode, kMenuControlGlyph},
  155.     {kShiftUnicode, kMenuShiftGlyph},
  156.     {kCheckUnicode, kCheckCharCode},
  157.     {kDiamondUnicode, kDiamondCharCode},
  158.     {kBulletUnicode, kBulletCharCode},
  159.     {0x2026, kNullCharCode},
  160.     {0, 0},
  161. };
  162. enum MenuSymbolIdx {
  163.     COMMAND_SYMBOL,
  164.     OPTION_SYMBOL,
  165.     CONTROL_SYMBOL,
  166.     SHIFT_SYMBOL,
  167.     CHECK_SYMBOL,
  168.     DIAMDOND_SYMBOL,
  169.     BULLET_SYMBOL,
  170.     ELLIPSIS_SYMBOL,
  171. };
  172. MenuRef tkCurrentAppleMenu = NULL;
  173. static SInt32 menuMarkColumnWidth = 0, menuMarkIndent = 0;
  174. static SInt32 menuTextLeadingEdgeMargin = 0, menuTextTrailingEdgeMargin = 0;
  175. static SInt16 menuItemExtraHeight = 0, menuItemExtraWidth = 0;
  176. static SInt16 menuSeparatorHeight = 0;
  177. /*
  178.  * Forward declarations for procedures defined later in this file:
  179.  */
  180. MODULE_SCOPE int TkMacOSXGetNewMenuID(Tcl_Interp *interp, TkMenu *menuInstPtr,
  181. int cascade, short *menuIDPtr);
  182. MODULE_SCOPE void TkMacOSXFreeMenuID(short menuID);
  183. static void CompleteIdlers(TkMenu *menuPtr);
  184. static void DrawMenuBarWhenIdle(ClientData clientData);
  185. static void DrawMenuEntryAccelerator(TkMenu *menuPtr, TkMenuEntry *mePtr,
  186. Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr,
  187. Tk_3DBorder activeBorder, int x, int y, int width, int height,
  188. int drawArrow);
  189. static void DrawMenuEntryBackground(TkMenu *menuPtr, TkMenuEntry *mePtr,
  190. Drawable d, Tk_3DBorder activeBorder, Tk_3DBorder bgBorder, int x,
  191. int y, int width, int heigth);
  192. static void DrawMenuEntryIndicator(TkMenu *menuPtr, TkMenuEntry *mePtr,
  193. Drawable d, GC gc, GC indicatorGC, Tk_Font tkfont,
  194. const Tk_FontMetrics *fmPtr, int x, int y, int width, int height);
  195. static void DrawMenuEntryLabel(TkMenu * menuPtr, TkMenuEntry *mePtr,
  196. Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x,
  197. int y, int width, int height);
  198. static void DrawMenuSeparator(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d,
  199. GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y,
  200. int width, int height);
  201. static void DrawTearoffEntry(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d,
  202. GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y,
  203. int width, int height);
  204. static void EventuallyInvokeMenu(ClientData data);
  205. static void GetEntryText(TkMenuEntry *mePtr, Tcl_DString *dStringPtr);
  206. static void GetMenuAccelGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr,
  207. Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *modWidthPtr,
  208. int *textWidthPtr, int *heightPtr);
  209. static void GetMenuLabelGeometry(TkMenuEntry *mePtr, Tk_Font tkfont,
  210. const Tk_FontMetrics *fmPtr, int *widthPtr, int *heightPtr);
  211. static void GetMenuIndicatorGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr,
  212. Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr,
  213. int *heightPtr);
  214. static void GetMenuSeparatorGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr,
  215. Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr,
  216. int *heightPtr);
  217. static TkMenuEntry* GetParentMenuEntry(TkMenu *menuPtr);
  218. static void GetTearoffEntryGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr,
  219. Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr,
  220. int *heightPtr);
  221. static char FindMarkCharacter(TkMenuEntry *mePtr);
  222. static int GetUtfMarkCharacter(char markChar, const char **markUtfPtr);
  223. static TkMenu* MenuPtrForMenuRef(MenuRef menu);
  224. static int ParseAccelerators(const char **accelStringPtr, int *modifierNumPtr,
  225. Tcl_UniChar *modifierUniChars, int *modifierWidth);
  226. static void MenuSelectEvent(TkMenu *menuPtr);
  227. static void ReconfigureIndividualMenu(TkMenu *menuPtr, MenuHandle macMenuHdl,
  228. int base);
  229. static void ReconfigureMacintoshMenu(ClientData clientData);
  230. static void RecursivelyClearActiveMenu(TkMenu *menuPtr);
  231. static void RecursivelyDeleteMenu(TkMenu *menuPtr);
  232. static void RecursivelyInsertMenu(TkMenu *menuPtr);
  233. static void SetDefaultMenubar(void);
  234. static int SetMenuCascade(TkMenu *menuPtr);
  235. #ifdef USE_TK_MDEF
  236. #define SCREEN_MARGIN 5
  237. static MacDrawable macMDEFDrawable;
  238. /* Drawable for use by MDEF code */
  239. static int MDEFScrollFlag = 0; /* Used so that popups don't scroll too soon.*/
  240. static MenuItemDrawingUPP tkThemeMenuItemDrawingUPP;
  241. /* Points to the UPP for theme Item drawing. */
  242. static Tcl_Obj *useMDEFVar;
  243. static void  DrawMenuBackground(TkMenu *menuPtr, Rect *menuRectPtr,
  244. Drawable d);
  245. static void MenuDefProc(short message, MenuHandle menu, Rect *menuRectPtr,
  246. Point hitPt, short *whichItem );
  247. static void HandleMenuHiliteMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt,
  248. SInt16 *whichItem, TkMenu *menuPtr);
  249. static void HandleMenuDrawMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt,
  250. SInt16 *whichItem, TkMenu *menuPtr);
  251. static void HandleMenuFindItemMsg(MenuRef menu, Rect *menuRectPtr,
  252. Point hitPt, SInt16 *whichItem, TkMenu *menuPtr);
  253. static void HandleMenuPopUpMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt,
  254. SInt16 *whichItem, TkMenu *menuPtr);
  255. static void HandleMenuCalcItemMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt,
  256. SInt16 *whichItem, TkMenu *menuPtr);
  257. static void AppearanceEntryDrawWrapper(TkMenuEntry *mePtr, Rect * menuRectPtr,
  258. MenuTrackingData *mtdPtr, Drawable d, Tk_FontMetrics *fmPtr,
  259. Tk_Font tkfont, int erase);
  260. static pascal void ThemeMenuItemDrawingProc(const Rect *inBounds,
  261. SInt16 inDepth, Boolean inIsColorDevice, SInt32 inUserData);
  262. #else /* USE_TK_MDEF */
  263. #   define useMDEF 0
  264. #endif /* USE_TK_MDEF */
  265. #define IS_THEME_MENU_FONT(tkfont) (strcmp(Tk_NameOfFont(tkfont), "menu") == 0)
  266. /*
  267.  *----------------------------------------------------------------------
  268.  *
  269.  * DrawThemeText --
  270.  *
  271.  * Wrapper for DrawThemeTextBox API.
  272.  *
  273.  * Results:
  274.  * None.
  275.  *
  276.  * Side effects:
  277.  * None.
  278.  *
  279.  *----------------------------------------------------------------------
  280.  */
  281. static void
  282. DrawThemeText(
  283.     Drawable d,
  284.     GC gc,
  285.     CFStringRef string,
  286.     ThemeFontID font,
  287.     ThemeDrawState drawState,
  288.     const Rect* bounds,
  289.     int baseline,
  290.     int just)
  291. {
  292.     TkMacOSXDrawingContext dc;
  293.     Rect adjustedBounds;
  294.     /*
  295.      * Menu item text drawn with the .Keyboard font (used for
  296.      * kThemeMenuItemCmdKeyFont) won't always have the same ascent and
  297.      * baseline as text drawn with the regular menu item font, since the
  298.      * glyphs in the .Keyboard font may have a different height. Therefore, we
  299.      * first determine the baseline of the text and then adjust the bounds
  300.      * rect so the baseline aligns with the overall baseline of the menu item.
  301.      */
  302.     if (font == kThemeMenuItemCmdKeyFont) {
  303. Point size;
  304. SInt16 cmdKeyBaseline;
  305. GetThemeTextDimensions(string, font, drawState, false, &size,
  306. &cmdKeyBaseline);
  307. adjustedBounds = *bounds;
  308. OffsetRect(&adjustedBounds, 0, baseline - bounds->top - size.v -
  309. cmdKeyBaseline);
  310. bounds = &adjustedBounds;
  311.     }
  312.     if (TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) {
  313. ChkErr(DrawThemeTextBox, string, font, drawState, false, bounds, just,
  314. dc.context);
  315. TkMacOSXRestoreDrawingContext(&dc);
  316.     }
  317. }
  318. /*
  319.  *----------------------------------------------------------------------
  320.  *
  321.  * MeasureThemeText --
  322.  *
  323.  * Wrapper for GetThemeTextDimensions API.
  324.  *
  325.  * Results:
  326.  * None.
  327.  *
  328.  * Side effects:
  329.  * None.
  330.  *
  331.  *----------------------------------------------------------------------
  332.  */
  333. static int
  334. MeasureThemeText(
  335.     CFStringRef string,
  336.     ThemeFontID font)
  337. {
  338.     Point pt;
  339.     ChkErr(GetThemeTextDimensions, string, font, kThemeStateActive, false, &pt,
  340. NULL);
  341.     return pt.h;
  342. }
  343. /*
  344.  *----------------------------------------------------------------------
  345.  *
  346.  * TkMacOSXUseID --
  347.  *
  348.  * Take the ID out of the available list for new menus. Used by the
  349.  * default menu bar's menus so that they do not get created at the tk
  350.  * level. See TkMacOSXGetNewMenuID for more information.
  351.  *
  352.  * Results:
  353.  * Returns TCL_OK if the id was not in use. Returns TCL_ERROR if the
  354.  * id was in use.
  355.  *
  356.  * Side effects:
  357.  * A hash table entry in the command table is created with a NULL
  358.  * value.
  359.  *
  360.  *----------------------------------------------------------------------
  361.  */
  362. int
  363. TkMacOSXUseMenuID(
  364.     short macID) /* The id to take out of the table */
  365. {
  366.     Tcl_HashEntry *commandEntryPtr;
  367.     int newEntry;
  368.     int iMacID = macID; /* Do this to remove compiler warning */
  369.     TkMenuInit();
  370.     commandEntryPtr = Tcl_CreateHashEntry(&commandTable, (char *) iMacID,
  371.     &newEntry);
  372.     if (!newEntry) {
  373. return TCL_ERROR;
  374.     }
  375.     Tcl_SetHashValue(commandEntryPtr, NULL);
  376.     return TCL_OK;
  377. }
  378. /*
  379.  *----------------------------------------------------------------------
  380.  *
  381.  * TkMacOSXGetNewMenuID --
  382.  *
  383.  * Allocates a new menu id and marks it in use. Each menu on the
  384.  * mac must be designated by a unique id, which is a short. In
  385.  * addition, some ids are reserved by the system. Since Tk uses
  386.  * mostly dynamic menus, we must allocate and free these ids on
  387.  * the fly. We use the id as a key into a hash table; if there
  388.  * is no hash entry, we know that we can use the id.
  389.  *
  390.  * Carbon allows a much larger number of menus than the old APIs.
  391.  * I believe this is 32768, but am not sure. This code just uses
  392.  * 2000 as the upper limit. Unfortunately tk leaks menus when
  393.  * cloning, under some circumstances (see bug on sourceforge).
  394.  *
  395.  * Results:
  396.  * Returns TCL_OK if succesful; TCL_ERROR if there are no more
  397.  * ids of the appropriate type to allocate. menuIDPtr contains
  398.  * the new id if succesful.
  399.  *
  400.  * Side effects:
  401.  * An entry is created for the menu in the command hash table,
  402.  * and the hash entry is stored in the appropriate field in the
  403.  * menu data structure.
  404.  *
  405.  *----------------------------------------------------------------------
  406.  */
  407. int
  408.  TkMacOSXGetNewMenuID(
  409.     Tcl_Interp *interp, /* Used for error reporting */
  410.     TkMenu *menuPtr, /* The menu we are working with */
  411.     int cascade, /* 0 if we are working with a normal menu;
  412.  * 1 if we are working with a cascade */
  413.     short *menuIDPtr) /* The resulting id */
  414. {
  415.     int found = 0;
  416.     int newEntry;
  417.     Tcl_HashEntry *commandEntryPtr = NULL;
  418.     short returnID = *menuIDPtr;
  419.     /*
  420.      * The following code relies on shorts and unsigned chars wrapping
  421.      * when the highest value is incremented. Also, the values between
  422.      * 236 and 255 inclusive are reserved for DA's by the Mac OS.
  423.      */
  424.     if (!cascade) {
  425. short curID = lastMenuID + 1;
  426. if (curID == 236) {
  427.     curID = 256;
  428. }
  429. while (curID != lastMenuID) {
  430.     int iCurID = curID;
  431.     commandEntryPtr = Tcl_CreateHashEntry(&commandTable,
  432.     (char *) iCurID, &newEntry);
  433.     if (newEntry == 1) {
  434. found = 1;
  435. lastMenuID = returnID = curID;
  436. break;
  437.     }
  438.     curID++;
  439.     if (curID == 236) {
  440. curID = 256;
  441.     }
  442. }
  443.     } else {
  444. /*
  445.  * Cascade ids must be between 0 and 235 only, so they must be
  446.  * dealt with separately.
  447.  */
  448. short curID = lastCascadeID + 1;
  449. if (curID == 2000) {
  450.     curID = 0;
  451. }
  452. while (curID != lastCascadeID) {
  453.     int iCurID = curID;
  454.     commandEntryPtr = Tcl_CreateHashEntry(&commandTable,
  455.     (char *) iCurID, &newEntry);
  456.     if (newEntry == 1) {
  457. found = 1;
  458. lastCascadeID = returnID = curID;
  459. break;
  460.     }
  461.     curID++;
  462.     if (curID == 2000) {
  463. curID = 0;
  464.     }
  465. }
  466.     }
  467.     if (!found) {
  468. Tcl_ResetResult(interp);
  469. Tcl_AppendResult(interp, "No more menus can be allocated.", NULL);
  470. return TCL_ERROR;
  471.     }
  472.     Tcl_SetHashValue(commandEntryPtr, (char *) menuPtr);
  473.     *menuIDPtr = returnID;
  474.     return TCL_OK;
  475. }
  476. /*
  477.  *----------------------------------------------------------------------
  478.  *
  479.  * TkMacOSXFreeMenuID --
  480.  *
  481.  * Marks the id as free.
  482.  *
  483.  * Results:
  484.  * None.
  485.  *
  486.  * Side effects:
  487.  * The hash table entry for the ID is cleared.
  488.  *
  489.  *----------------------------------------------------------------------
  490.  */
  491. void
  492. TkMacOSXFreeMenuID(
  493.     short menuID) /* The id to free */
  494. {
  495.     Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&commandTable,
  496.     (char*)(intptr_t)menuID);
  497.     if (entryPtr != NULL) {
  498.  Tcl_DeleteHashEntry(entryPtr);
  499.     }
  500.     if (menuID == currentAppleMenuID) {
  501. currentAppleMenuID = 0;
  502.     }
  503.     if (menuID == currentHelpMenuID) {
  504. currentHelpMenuID = 0;
  505.     }
  506. }
  507. /*
  508.  *----------------------------------------------------------------------
  509.  *
  510.  * MenuPtrForMenuRef --
  511.  *
  512.  * Returns a pointer to the TkMenu corresponding to a given
  513.  * Carbon MenuRef.
  514.  *
  515.  * Results:
  516.  * Returns a pointer to a TkMenu or NULL.
  517.  *
  518.  * Side effects:
  519.  * None.
  520.  *
  521.  *----------------------------------------------------------------------
  522.  */
  523. TkMenu*
  524. MenuPtrForMenuRef(
  525.     MenuRef menu)
  526. {
  527.     TkMenu *menuPtr = NULL;
  528.     MenuID menuID = GetMenuID(menu);
  529.     Tcl_HashEntry *commandEntryPtr = Tcl_FindHashEntry(&commandTable,
  530.     (char*)(intptr_t)menuID);
  531.     if (commandEntryPtr) {
  532. menuPtr = (TkMenu *) Tcl_GetHashValue(commandEntryPtr);
  533.     }
  534.     return menuPtr;
  535. }
  536. /*
  537.  *----------------------------------------------------------------------
  538.  *
  539.  * GetParentMenuEntry --
  540.  *
  541.  * Returns a pointer to the parent's TkMenuEntry of a given TkMenu.
  542.  *
  543.  * Results:
  544.  * Returns a pointer to a TkMenuEntry or NULL.
  545.  *
  546.  * Side effects:
  547.  * None.
  548.  *
  549.  *----------------------------------------------------------------------
  550.  */
  551. TkMenuEntry*
  552. GetParentMenuEntry(
  553.     TkMenu *menuPtr)
  554. {
  555.     TkMenuEntry *cascadeEntryPtr;
  556.     for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
  557.     cascadeEntryPtr != NULL;
  558.     cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
  559. const char *name = (cascadeEntryPtr->namePtr == NULL) ? ""
  560. : Tcl_GetString(cascadeEntryPtr->namePtr);
  561. if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) {
  562.     break;
  563. }
  564.     }
  565.     return cascadeEntryPtr;
  566. }
  567. /*
  568.  *----------------------------------------------------------------------
  569.  *
  570.  * TkpNewMenu --
  571.  *
  572.  * Gets a new blank menu. Only the platform specific options are filled
  573.  * in.
  574.  *
  575.  * Results:
  576.  * Returns a standard TCL error.
  577.  *
  578.  * Side effects:
  579.  * Allocates a Macintosh menu handle and puts in the platformData
  580.  * field of the menuPtr.
  581.  *
  582.  *----------------------------------------------------------------------
  583.  */
  584. int
  585. TkpNewMenu(
  586.     TkMenu *menuPtr) /* The common structure we are making the
  587.  * platform structure for. */
  588. {
  589.     short menuID;
  590.     MenuRef macMenuHdl;
  591. #ifdef USE_TK_MDEF
  592.     MenuDefSpec menuDefSpec;
  593.     Tcl_Obj *useMDEFObjPtr;
  594.     int useMDEF = 1;
  595. #endif
  596.     int error = TCL_OK;
  597.     OSStatus err;
  598.     CFStringRef cfStr;
  599.     error = TkMacOSXGetNewMenuID(menuPtr->interp, menuPtr, 0, &menuID);
  600.     if (error != TCL_OK) {
  601. return error;
  602.     }
  603.     err = ChkErr(CreateNewMenu, menuID, kMenuAttrDoNotUseUserCommandKeys,
  604.     &macMenuHdl);
  605.     if (err != noErr) {
  606. Tcl_AppendResult(menuPtr->interp, "CreateNewMenu failed.", NULL);
  607. return TCL_ERROR;
  608.     }
  609.     cfStr = CFStringCreateWithCString(NULL, Tk_PathName(menuPtr->tkwin),
  610.     kCFStringEncodingUTF8);
  611.     if (!cfStr) {
  612. Tcl_AppendResult(menuPtr->interp, "CFStringCreateWithCString failed.",
  613. NULL);
  614. return TCL_ERROR;
  615.     }
  616.     err = ChkErr(SetMenuTitleWithCFString, macMenuHdl, cfStr);
  617.     CFRelease(cfStr);
  618.     if (err != noErr) {
  619. Tcl_AppendResult(menuPtr->interp, "SetMenuTitleWithCFString failed.",
  620. NULL);
  621. return TCL_ERROR;
  622.     }
  623.     menuPtr->platformData = (TkMenuPlatformData) ckalloc(sizeof(MacMenu));
  624.     ((MacMenu *) menuPtr->platformData)->menuHdl = macMenuHdl;
  625. #ifdef USE_TK_MDEF
  626.     /*
  627.      * Check whether we want to use the custom mdef or not. For now
  628.      * the default is to use it unless the variable is explicitly
  629.      * set to no.
  630.      */
  631.     useMDEFObjPtr = Tcl_ObjGetVar2(menuPtr->interp, useMDEFVar, NULL,
  632.     TCL_GLOBAL_ONLY);
  633.     if (useMDEFObjPtr == NULL || Tcl_GetBooleanFromObj(NULL, useMDEFObjPtr,
  634.     &useMDEF) == TCL_ERROR || useMDEF) {
  635. menuDefSpec.defType = kMenuDefProcPtr;
  636. menuDefSpec.u.defProc = MenuDefProc;
  637. ChkErr(SetMenuDefinition, macMenuHdl, &menuDefSpec);
  638.     }
  639.     ((MacMenu *) menuPtr->platformData)->useMDEF = useMDEF;
  640. #endif /* USE_TK_MDEF */
  641.     if ((currentMenuBarInterp == menuPtr->interp)
  642.     && (currentMenuBarName != NULL)) {
  643. Tk_Window parentWin = Tk_Parent(menuPtr->tkwin);
  644. if (strcmp(currentMenuBarName, Tk_PathName(parentWin)) == 0) {
  645.     if ((strcmp(Tk_PathName(menuPtr->tkwin)
  646.     + strlen(Tk_PathName(parentWin)), ".apple") == 0)
  647.     || (strcmp(Tk_PathName(menuPtr->tkwin)
  648.     + strlen(Tk_PathName(parentWin)), ".help") == 0)) {
  649. if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) {
  650.     Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL);
  651.     menuBarFlags |= MENUBAR_REDRAW_PENDING;
  652. }
  653.     }
  654. }
  655.     }
  656.     menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
  657.     Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr);
  658.     return TCL_OK;
  659. }
  660. /*
  661.  *----------------------------------------------------------------------
  662.  *
  663.  * TkpDestroyMenu --
  664.  *
  665.  * Destroys platform-specific menu structures.
  666.  *
  667.  * Results:
  668.  * None.
  669.  *
  670.  * Side effects:
  671.  * All platform-specific allocations are freed up.
  672.  *
  673.  *----------------------------------------------------------------------
  674.  */
  675. void
  676. TkpDestroyMenu(
  677.     TkMenu *menuPtr) /* The common menu structure */
  678. {
  679.     MenuRef macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl;
  680.     if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
  681. Tcl_CancelIdleCall(ReconfigureMacintoshMenu, (ClientData) menuPtr);
  682. menuPtr->menuFlags &= ~MENU_RECONFIGURE_PENDING;
  683.     }
  684.     if (GetMenuID(macMenuHdl) == currentHelpMenuID) {
  685. MenuRef helpMenuHdl;
  686. MenuItemIndex helpIndex;
  687. if ((HMGetHelpMenu(&helpMenuHdl,&helpIndex) == noErr)
  688. && (helpMenuHdl != NULL)) {
  689.     int i, count = CountMenuItems(helpMenuHdl);
  690.     for (i = helpIndex; i <= count; i++) {
  691. DeleteMenuItem(helpMenuHdl, helpIndex);
  692.     }
  693. }
  694. currentHelpMenuID = 0;
  695.     }
  696.     if (menuPtr->platformData != NULL) {
  697. MenuID menuID = GetMenuID(macMenuHdl);
  698. DeleteMenu(menuID);
  699. TkMacOSXFreeMenuID(menuID);
  700. DisposeMenu(macMenuHdl);
  701. ckfree((char *) menuPtr->platformData);
  702. menuPtr->platformData = NULL;
  703.     }
  704. }
  705. /*
  706.  *----------------------------------------------------------------------
  707.  *
  708.  * SetMenuCascade --
  709.  *
  710.  * Does any cleanup to change a menu from a normal to a cascade.
  711.  *
  712.  * Results:
  713.  * Standard Tcl error.
  714.  *
  715.  * Side effects:
  716.  * The mac menu id is reset.
  717.  *
  718.  *----------------------------------------------------------------------
  719.  */
  720. int
  721. SetMenuCascade(
  722.     TkMenu* menuPtr) /* The menu we are setting up to be a
  723.  * cascade. */
  724. {
  725.     MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl;
  726.     MenuID newMenuID, menuID = GetMenuID(macMenuHdl);
  727.     int error = TCL_OK;
  728.     if (menuID >= 256) {
  729. error = TkMacOSXGetNewMenuID(menuPtr->interp, menuPtr, 1, &newMenuID);
  730. if (error == TCL_OK) {
  731.     TkMacOSXFreeMenuID(menuID);
  732.     SetMenuID(macMenuHdl,newMenuID);
  733. }
  734.     }
  735.     return error;
  736. }
  737. /*
  738.  *----------------------------------------------------------------------
  739.  *
  740.  * TkpDestroyMenuEntry --
  741.  *
  742.  * Cleans up platform-specific menu entry items.
  743.  *
  744.  * Results:
  745.  * None
  746.  *
  747.  * Side effects:
  748.  * All platform-specific allocations are freed up.
  749.  *
  750.  *----------------------------------------------------------------------
  751.  */
  752. void
  753. TkpDestroyMenuEntry(
  754.     TkMenuEntry *mePtr) /* The common structure for the menu entry. */
  755. {
  756.     TkMenu *menuPtr = mePtr->menuPtr;
  757.     ckfree((char *) mePtr->platformEntryData);
  758.     if ((menuPtr->platformData != NULL)
  759.     && !(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
  760. menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
  761. Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr);
  762.     }
  763. }
  764. /*
  765.  *----------------------------------------------------------------------
  766.  *
  767.  * GetEntryText --
  768.  *
  769.  * Given a menu entry, gives back the text that should go in it.
  770.  * Separators should be done by the caller, as they have to be
  771.  * handled specially. This is primarily used to do a substitution
  772.  * between "..." and the ellipsis character which looks nicer.
  773.  *
  774.  * Results:
  775.  * itemText points to the new text for the item.
  776.  *
  777.  * Side effects:
  778.  * None.
  779.  *
  780.  *----------------------------------------------------------------------
  781.  */
  782. void
  783. GetEntryText(
  784.     TkMenuEntry *mePtr, /* A pointer to the menu entry. */
  785.     Tcl_DString *dStringPtr) /* The DString to put the text into. This
  786.  * will be initialized by this routine. */
  787. {
  788. #ifdef USE_TK_MDEF
  789.     const int useMDEF = ((MacMenu *) mePtr->menuPtr->platformData)->useMDEF;
  790. #endif
  791.     int noLabel = (mePtr->labelPtr == NULL || mePtr->labelLength == 0);
  792.     Tcl_DStringInit(dStringPtr);
  793.     if (mePtr->type == TEAROFF_ENTRY && (useMDEF || noLabel)) {
  794. Tcl_DStringAppend(dStringPtr, "(Tear-off)", -1);
  795.     } else if (mePtr->imagePtr != NULL && (useMDEF || noLabel) &&
  796.     mePtr->compound == COMPOUND_NONE) {
  797. Tcl_DStringAppend(dStringPtr, "(Image)", -1);
  798.     } else if (mePtr->bitmapPtr != NULL && (useMDEF || noLabel) &&
  799.     mePtr->compound == COMPOUND_NONE) {
  800. Tcl_DStringAppend(dStringPtr, "(Pixmap)", -1);
  801.     } else if (noLabel) {
  802. /*
  803.  * The Mac menu manager does not like null strings.
  804.  */
  805. Tcl_DStringAppend(dStringPtr, " ", -1);
  806.     } else {
  807. int length;
  808. char *text = Tcl_GetStringFromObj(mePtr->labelPtr, &length);
  809. char *dStringText;
  810. int i;
  811. for (i = 0; *text; text++, i++) {
  812.     if ((*text == '.') && (*(text+1) == '.') && (*(text+2) == '.')) {
  813. Tcl_DStringAppend(dStringPtr, menuSymbols[ELLIPSIS_SYMBOL].utf,
  814. menuSymbols[ELLIPSIS_SYMBOL].utfLen);
  815. i += menuSymbols[ELLIPSIS_SYMBOL].utfLen - 1;
  816. text += 2;
  817.     } else {
  818. Tcl_DStringSetLength(dStringPtr,
  819. Tcl_DStringLength(dStringPtr) + 1);
  820. dStringText = Tcl_DStringValue(dStringPtr);
  821. dStringText[i] = *text;
  822.     }
  823. }
  824.     }
  825. }
  826. /*
  827.  *----------------------------------------------------------------------
  828.  *
  829.  * FindMarkCharacter --
  830.  *
  831.  * Finds the Macintosh mark character based on the font of the
  832.  * item. We calculate a good mark character based on the font
  833.  * that this item is rendered in.
  834.  *
  835.  * Results:
  836.  * Mark char.
  837.  *
  838.  * Side effects:
  839.  * None.
  840.  *
  841.  *----------------------------------------------------------------------
  842.  */
  843. char
  844. FindMarkCharacter(
  845.     TkMenuEntry *mePtr) /* The entry we are finding the character
  846.  * for. */
  847. {
  848.     static const char markChars[] = {kCheckCharCode, kDiamondCharCode,
  849.     kBulletCharCode, '-', kCheckCharCode};
  850.     const char *markChar = markChars;
  851.     int i = sizeof(markChars);
  852.     Tk_Font tkfont;
  853.     tkfont = Tk_GetFontFromObj(mePtr->menuPtr->tkwin,
  854.     (mePtr->fontPtr == NULL) ? mePtr->menuPtr->fontPtr
  855.     : mePtr->fontPtr);
  856.     while (--i) {
  857. if (!TkMacOSXIsCharacterMissing(tkfont, *markChar)) {
  858.     break;
  859. }
  860. markChar++;
  861.     }
  862.     return *markChar;
  863. }
  864. /*
  865.  *----------------------------------------------------------------------
  866.  *
  867.  * GetUtfMarkCharacter --
  868.  *
  869.  * Get the utf8 string for the given mark character, taking into
  870.  * account the special menu font char codes.
  871.  *
  872.  * Results:
  873.  * Length of returned utf8 string.
  874.  *
  875.  * Side effects:
  876.  * None.
  877.  *
  878.  *----------------------------------------------------------------------
  879.  */
  880. int
  881. GetUtfMarkCharacter(
  882.     char markChar,
  883.     const char **markUtfPtr)
  884. {
  885.     const MenuSymbol *ms = menuSymbols;
  886.     int len = 0;
  887.     while (ms->unicode) {
  888. if (ms->charCode && ms->charCode == markChar) {
  889.     *markUtfPtr = ms->utf;
  890.     len = ms->utfLen;
  891.     break;
  892. }
  893. ms++;
  894.     }
  895.     if (!len) {
  896. static char markUtf[TCL_UTF_MAX + 1];
  897. Tcl_ExternalToUtf(NULL, TkMacOSXCarbonEncoding, &markChar, 1, 0, NULL,
  898. markUtf, TCL_UTF_MAX + 1, NULL, &len, NULL);
  899. *markUtfPtr = markUtf;
  900.     }
  901.     return len;
  902. }
  903. /*
  904.  *----------------------------------------------------------------------
  905.  *
  906.  * ParseAccelerators --
  907.  *
  908.  * Parse menu accelerator string.
  909.  *
  910.  * Results:
  911.  * Accelerator flags.
  912.  *
  913.  * Side effects:
  914.  * None.
  915.  *
  916.  *----------------------------------------------------------------------
  917.  */
  918. int
  919. ParseAccelerators(
  920.     const char **accelStringPtr,
  921.     int *modifierNumPtr,
  922.     Tcl_UniChar *modifierUniChars,
  923.     int *modifierWidth)
  924. {
  925.     struct Modif {
  926. const char *name;
  927. const size_t len;
  928. const int flag, symbol;
  929.     };
  930. #define MODIF(n, f) { #n, sizeof(#n)-1, ENTRY_##f##_ACCEL, f##_SYMBOL }
  931.     static const struct Modif modifs[] = {
  932. MODIF(Control, CONTROL),
  933. MODIF(Ctrl, CONTROL),
  934. MODIF(Option, OPTION),
  935. MODIF(Opt, OPTION),
  936. MODIF(Alt, OPTION),
  937. MODIF(Shift, SHIFT),
  938. MODIF(Command, COMMAND),
  939. MODIF(Cmd, COMMAND),
  940. MODIF(Meta, COMMAND),
  941. { NULL, 0, 0, 0}
  942.     };
  943. #undef MODIF
  944.     const char *accelString = *accelStringPtr;
  945.     int flags = 0, num = 0, seen = 0, width = 0;
  946.     const struct Modif *m;
  947.     while (1) {
  948. m = modifs;
  949. while (m->name) {
  950.     int l = m->len;
  951.     if (!strncasecmp(accelString, m->name, l) &&
  952.     (accelString[l] == '-' || accelString[l] == '+')) {
  953. flags |= m->flag;
  954. accelString += l+1;
  955. break;
  956.     }
  957.     m++;
  958. }
  959. if (!m->name || !*accelString) {
  960.     break;
  961. }
  962.     }
  963.     m = modifs;
  964.     while (m->name && num < MODIFIER_NUM) {
  965. if (flags & m->flag && !(seen & m->flag)) {
  966.     modifierUniChars[num++] = menuSymbols[m->symbol].unicode;
  967.     width += menuSymbols[m->symbol].width;
  968.     seen |= m->flag;
  969. }
  970. m++;
  971.     }
  972.     *accelStringPtr = accelString;
  973.     *modifierNumPtr = num;
  974.     *modifierWidth = width;
  975.     return flags;
  976. }
  977. /*
  978.  *----------------------------------------------------------------------
  979.  *
  980.  * TkpConfigureMenuEntry --
  981.  *
  982.  * Processes configurations for menu entries.
  983.  *
  984.  * Results:
  985.  * Returns standard TCL result. If TCL_ERROR is returned, then
  986.  * the interp's result contains an error message.
  987.  *
  988.  * Side effects:
  989.  * Configuration information get set for mePtr; old resources
  990.  * get freed, if any need it.
  991.  *
  992.  *----------------------------------------------------------------------
  993.  */
  994. int
  995. TkpConfigureMenuEntry(
  996.     TkMenuEntry *mePtr)  /* Information about menu entry; may
  997.  * or may not already have values for
  998.  * some fields. */
  999. {
  1000.     TkMenu *menuPtr = mePtr->menuPtr;
  1001.     EntryGeometry *geometryPtr = (EntryGeometry *) mePtr->platformEntryData;
  1002.     /*
  1003.      * Cascade menus have to have menu IDs of less than 256. So
  1004.      * we need to change the child menu if this has been configured
  1005.      * for a cascade item.
  1006.      */
  1007.     if (mePtr->type == CASCADE_ENTRY) {
  1008. if ((mePtr->childMenuRefPtr != NULL)
  1009. && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
  1010.     MenuHandle childMenuHdl = ((MacMenu *) mePtr
  1011.     ->childMenuRefPtr->menuPtr->platformData)->menuHdl;
  1012.     if (childMenuHdl != NULL) {
  1013. int error = SetMenuCascade(mePtr->childMenuRefPtr->menuPtr);
  1014. if (error != TCL_OK) {
  1015.     return error;
  1016. }
  1017. if (menuPtr->menuType == MENUBAR) {
  1018.     CFStringRef cfStr = CFStringCreateWithCString(NULL,
  1019.     (!(mePtr->labelPtr) ? "" :
  1020.     Tcl_GetString(mePtr->labelPtr)),
  1021.     kCFStringEncodingUTF8);
  1022.     if (cfStr) {
  1023. SetMenuTitleWithCFString(childMenuHdl, cfStr);
  1024. CFRelease(cfStr);
  1025.     }
  1026. }
  1027.     }
  1028. }
  1029.     }
  1030.     /*
  1031.      * We need to parse the accelerator string. If it has the strings
  1032.      * for Command, Control, Shift or Option, we need to flag it
  1033.      * so we can draw the symbols for it. We also need to precalcuate
  1034.      * the position of the first real character we are drawing.
  1035.      */
  1036.     if (0 == mePtr->accelLength) {
  1037. geometryPtr->accelTextStart = -1;
  1038.     } else {
  1039. const char *accelString = (mePtr->accelPtr == NULL) ? ""
  1040. : Tcl_GetString(mePtr->accelPtr);
  1041. const char *accelStart = accelString;
  1042. mePtr->entryFlags &= ~ENTRY_ACCEL_MASK;
  1043. mePtr->entryFlags |= ParseAccelerators(&accelString,
  1044. &geometryPtr->modifierNum, geometryPtr->modifierUniChars,
  1045. &geometryPtr->modifierWidth);
  1046. geometryPtr->accelTextStart = (ptrdiff_t)(accelString - accelStart);
  1047.     }
  1048.     if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
  1049. menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
  1050. Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr);
  1051.     }
  1052.     return TCL_OK;
  1053. }
  1054. /*
  1055.  *----------------------------------------------------------------------
  1056.  *
  1057.  * ReconfigureIndividualMenu --
  1058.  *
  1059.  * This routine redoes the guts of the menu. It works from
  1060.  * a base item and offset, so that a regular menu will
  1061.  * just have all of its items added, but the help menu will
  1062.  * have all of its items appended after the apple-defined
  1063.  * items.
  1064.  *
  1065.  * Results:
  1066.  * None.
  1067.  *
  1068.  * Side effects:
  1069.  * The Macintosh menu handle is updated
  1070.  *
  1071.  *----------------------------------------------------------------------
  1072.  */
  1073. void
  1074. ReconfigureIndividualMenu(
  1075.     TkMenu *menuPtr, /* The menu we are affecting. */
  1076.     MenuHandle macMenuHdl, /* The macintosh menu we are affecting.
  1077.  * Will not necessarily be
  1078.  * menuPtr->platformData because this could
  1079.  * be the help menu. */
  1080.     int base) /* The last index that we do not want
  1081.  * touched. 0 for normal menus;
  1082.  * # of system help menu items
  1083.  * for help menus. */
  1084. {
  1085.     int count;
  1086.     int index;
  1087.     TkMenuEntry *mePtr;
  1088.     int parentDisabled = 0;
  1089. #ifdef TK_MAC_DEBUG_MENUS
  1090.     /*
  1091.      * Carbon-internal menu debugging (c.f. Technote 2124)
  1092.      */
  1093.     TkMacOSXInitNamedDebugSymbol(HIToolbox, void, DebugPrintMenu,
  1094. MenuRef menu);
  1095.     if (DebugPrintMenu) {
  1096. DebugPrintMenu(macMenuHdl);
  1097.     }
  1098. #endif
  1099.     mePtr = GetParentMenuEntry(menuPtr);
  1100.     if (mePtr && mePtr->state == ENTRY_DISABLED) {
  1101. parentDisabled = 1;
  1102.     }
  1103.     /*
  1104.      * First, we get rid of all of the old items.
  1105.      */
  1106.     count = CountMenuItems(macMenuHdl);
  1107.     for (index = base; index < count; index++) {
  1108. DeleteMenuItem(macMenuHdl, base + 1);
  1109.     }
  1110.     count = menuPtr->numEntries;
  1111.     for (index = 1; index <= count; index++) {
  1112. mePtr = menuPtr->entries[index - 1];
  1113. /*
  1114.  * We have to do separators separately because SetMenuItemText
  1115.  * does not parse meta-characters.
  1116.  */
  1117. if (mePtr->type == SEPARATOR_ENTRY) {
  1118.     AppendMenuItemTextWithCFString(macMenuHdl, NULL,
  1119.     kMenuItemAttrSeparator | kMenuItemAttrDisabled, 0, NULL);
  1120. } else {
  1121.     Tcl_DString itemTextDString;
  1122.     CFStringRef cfStr;
  1123.     GetEntryText(mePtr, &itemTextDString);
  1124.     cfStr = CFStringCreateWithCString(NULL,
  1125.     Tcl_DStringValue(&itemTextDString), kCFStringEncodingUTF8);
  1126.     if (cfStr) {
  1127. AppendMenuItemTextWithCFString(macMenuHdl, cfStr, 0, 0, NULL);
  1128. CFRelease(cfStr);
  1129.     } else {
  1130. AppendMenuItemTextWithCFString(macMenuHdl, CFSTR ("<Error>"),
  1131.     0, 0, NULL);
  1132.     }
  1133.     Tcl_DStringFree(&itemTextDString);
  1134.     /*
  1135.      * Set enabling and disabling correctly.
  1136.      */
  1137.     if (parentDisabled || (mePtr->state == ENTRY_DISABLED)) {
  1138. DisableMenuItem(macMenuHdl, base + index);
  1139.     } else {
  1140. EnableMenuItem(macMenuHdl, base + index);
  1141.     }
  1142.     /*
  1143.      * Set the check mark for check entries and radio entries.
  1144.      */
  1145.     SetItemMark(macMenuHdl, base + index, 0);
  1146.     if ((mePtr->type == CHECK_BUTTON_ENTRY)
  1147.     || (mePtr->type == RADIO_BUTTON_ENTRY)) {
  1148. CheckMenuItem(macMenuHdl, base + index, (mePtr->entryFlags
  1149. & ENTRY_SELECTED) && mePtr->indicatorOn);
  1150. if (mePtr->indicatorOn
  1151. && (mePtr->entryFlags & ENTRY_SELECTED)) {
  1152.     SetItemMark(macMenuHdl, base + index,
  1153.     FindMarkCharacter(mePtr));
  1154. }
  1155.     }
  1156.     if (mePtr->type == CASCADE_ENTRY) {
  1157. if ((mePtr->childMenuRefPtr != NULL)
  1158. && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
  1159.     MenuHandle childMenuHdl =
  1160.     ((MacMenu *) mePtr->childMenuRefPtr
  1161.     ->menuPtr->platformData)->menuHdl;
  1162.     if (childMenuHdl != NULL) {
  1163. ChkErr(SetMenuItemHierarchicalID, macMenuHdl,
  1164. base + index, GetMenuID(childMenuHdl));
  1165.     }
  1166.     /*
  1167.      * If we changed the highligthing of this menu, its
  1168.      * children all have to be reconfigured so that
  1169.      * their state will be reflected in the menubar.
  1170.      */
  1171.     if (!(mePtr->childMenuRefPtr->menuPtr->menuFlags
  1172. & MENU_RECONFIGURE_PENDING)) {
  1173. mePtr->childMenuRefPtr->menuPtr->menuFlags
  1174. |= MENU_RECONFIGURE_PENDING;
  1175. Tcl_DoWhenIdle(ReconfigureMacintoshMenu,
  1176. (ClientData) mePtr->childMenuRefPtr->menuPtr);
  1177.     }
  1178. }
  1179.     }
  1180.     if ((mePtr->type != CASCADE_ENTRY) && (mePtr->accelPtr != NULL)) {
  1181. int accelLen, modifiers = 0, hasCmd = 0;
  1182. EntryGeometry *geometryPtr =
  1183. (EntryGeometry*)mePtr->platformEntryData;
  1184. int offset = geometryPtr->accelTextStart;
  1185. char *accel = Tcl_GetStringFromObj(mePtr->accelPtr, &accelLen);
  1186. accelLen -= offset;
  1187. accel += offset;
  1188. if (mePtr->entryFlags & ENTRY_OPTION_ACCEL) {
  1189.     modifiers |= kMenuOptionModifier;
  1190. }
  1191. if (mePtr->entryFlags & ENTRY_SHIFT_ACCEL) {
  1192.     modifiers |= kMenuShiftModifier;
  1193. }
  1194. if (mePtr->entryFlags & ENTRY_CONTROL_ACCEL) {
  1195.     modifiers |= kMenuControlModifier;
  1196. }
  1197. if (mePtr->entryFlags & ENTRY_COMMAND_ACCEL) {
  1198.     hasCmd = 1;
  1199. }
  1200. if (accelLen == 1) {
  1201.     if (hasCmd || (modifiers != 0 && modifiers !=
  1202.     kMenuShiftModifier)) {
  1203. SetItemCmd(macMenuHdl, base + index, accel[0]);
  1204. if (!hasCmd) {
  1205.     modifiers |= kMenuNoCommandModifier;
  1206. }
  1207.     }
  1208. } else {
  1209.     /*
  1210.      * Convert from accelerator names to Carbon menu glyphs.
  1211.      */
  1212.     struct Glyph {
  1213. const char *name;
  1214. const size_t len;
  1215. const char glyph;
  1216.     };
  1217. #define GLYPH(n, g) { #n, sizeof(#n)-1, kMenu##g##Glyph }
  1218.     static const struct Glyph glyphs[] = {
  1219. GLYPH(PageUp, PageUp),
  1220. GLYPH(PageDown, PageDown),
  1221. GLYPH(Left, LeftArrow),
  1222. GLYPH(Right, RightArrow),
  1223. GLYPH(Up, UpArrow),
  1224. GLYPH(Down, DownArrow),
  1225. GLYPH(Escape, Escape),
  1226. GLYPH(Clear, Clear),
  1227. GLYPH(Enter, Enter),
  1228. GLYPH(Backspace,DeleteLeft),
  1229. GLYPH(Space, Space),
  1230. GLYPH(Tab, TabRight),
  1231. GLYPH(Delete, DeleteRight),
  1232. GLYPH(Home, NorthwestArrow),
  1233. GLYPH(End, SoutheastArrow),
  1234. GLYPH(Return, Return),
  1235. GLYPH(Help, Help),
  1236. GLYPH(Power, Power),
  1237. { NULL, 0, 0}
  1238.     };
  1239. #undef GLYPH
  1240.     const struct Glyph *g = glyphs;
  1241.     char glyph = 0;
  1242.     if (accel[0] == 'F' && accelLen < 4 &&
  1243.     (accel[1] > '0' && accel[1] <= '9')) {
  1244. int fkey = accel[1] - '0';
  1245. if (accelLen == 3) {
  1246.     if (accel[2] >= '0' && accel[2] <= '9') {
  1247. fkey = 10 * fkey + (accel[2] - '0');
  1248.     } else {
  1249. fkey = 0;
  1250.     }
  1251. }
  1252. if (fkey >= 1 && fkey <= 12) {
  1253.     glyph = kMenuF1Glyph + fkey - 1;
  1254. } else if (fkey >= 13 && fkey <= 15) {
  1255.     glyph = kMenuF13Glyph + fkey - 13;
  1256. }
  1257.     } else while (g->name) {
  1258. if (accel[0] == g->name[0] &&
  1259. (size_t)accelLen == g->len &&
  1260. !strncasecmp(accel, g->name, g->len)) {
  1261.     glyph = g->glyph;
  1262.     break;
  1263. }
  1264. g++;
  1265.     }
  1266.     if (glyph) {
  1267. ChkErr(SetMenuItemKeyGlyph, macMenuHdl, base + index,
  1268. glyph);
  1269. if (!hasCmd) {
  1270.     modifiers |= kMenuNoCommandModifier;
  1271. }
  1272. geometryPtr->accelGlyph = glyph;
  1273.     }
  1274. }
  1275. ChkErr(SetMenuItemModifiers, macMenuHdl, base + index,
  1276. modifiers);
  1277.     }
  1278. }
  1279.     }
  1280. }
  1281. /*
  1282.  *----------------------------------------------------------------------
  1283.  *
  1284.  * ReconfigureMacintoshMenu --
  1285.  *
  1286.  * Rebuilds the Macintosh MenuHandle items from the menu. Called
  1287.  * usually as an idle handler, but can be called synchronously
  1288.  * if the menu is about to be posted.
  1289.  *
  1290.  * Results:
  1291.  * None.
  1292.  *
  1293.  * Side effects:
  1294.  * Configuration information get set for mePtr; old resources
  1295.  * get freed, if any need it.
  1296.  *
  1297.  *----------------------------------------------------------------------
  1298.  */
  1299. void
  1300. ReconfigureMacintoshMenu(
  1301.     ClientData clientData) /* Information about menu entry; may
  1302.  * or may not already have values for
  1303.  * some fields. */
  1304. {
  1305.     TkMenu *menuPtr = (TkMenu *) clientData;
  1306.     MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl;
  1307.     MenuHandle helpMenuHdl = NULL;
  1308.     menuPtr->menuFlags &= ~MENU_RECONFIGURE_PENDING;
  1309.     if (NULL == macMenuHdl) {
  1310. return;
  1311.     }
  1312.     ReconfigureIndividualMenu(menuPtr, macMenuHdl, 0);
  1313.     if (GetMenuID(macMenuHdl) == currentHelpMenuID) {
  1314. MenuItemIndex helpIndex;
  1315. HMGetHelpMenu(&helpMenuHdl,&helpIndex);
  1316. if (helpMenuHdl != NULL) {
  1317.     ReconfigureIndividualMenu(menuPtr, helpMenuHdl, helpIndex - 1);
  1318. }
  1319.     }
  1320.     if (menuPtr->menuType == MENUBAR) {
  1321. if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) {
  1322.     Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL);
  1323.     menuBarFlags |= MENUBAR_REDRAW_PENDING;
  1324. }
  1325.     }
  1326. }
  1327. /*
  1328.  *----------------------------------------------------------------------
  1329.  *
  1330.  * CompleteIdlers --
  1331.  *
  1332.  * Completes all idle handling so that the menus are in sync when
  1333.  * the user invokes them with the mouse.
  1334.  *
  1335.  * Results:
  1336.  * None.
  1337.  *
  1338.  * Side effects:
  1339.  * The Macintosh menu handles are flushed out.
  1340.  *
  1341.  *----------------------------------------------------------------------
  1342.  */
  1343. void
  1344. CompleteIdlers(
  1345.     TkMenu *menuPtr) /* The menu we are completing. */
  1346. {
  1347.     int i;
  1348.     if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
  1349. Tcl_CancelIdleCall(ReconfigureMacintoshMenu, (ClientData) menuPtr);
  1350. ReconfigureMacintoshMenu((ClientData) menuPtr);
  1351.     }
  1352.     for (i = 0; i < menuPtr->numEntries; i++) {
  1353. if ((menuPtr->entries[i]->type == CASCADE_ENTRY) &&
  1354. (menuPtr->entries[i]->childMenuRefPtr != NULL) &&
  1355. (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) {
  1356.     CompleteIdlers(menuPtr->entries[i]->childMenuRefPtr->menuPtr);
  1357. }
  1358.     }
  1359. }
  1360. /*
  1361.  *----------------------------------------------------------------------
  1362.  *
  1363.  * TkpPostMenu --
  1364.  *
  1365.  * Posts a menu on the screen
  1366.  *
  1367.  * Results:
  1368.  * None.
  1369.  *
  1370.  * Side effects:
  1371.  * The menu is posted and handled.
  1372.  *
  1373.  *----------------------------------------------------------------------
  1374.  */
  1375. int
  1376. TkpPostMenu(
  1377.     Tcl_Interp *interp, /* The interpreter this menu lives in */
  1378.     TkMenu *menuPtr, /* The menu we are posting */
  1379.     int x, /* The global x-coordinate of the top, left-
  1380.  * hand corner of where the menu is supposed
  1381.  * to be posted. */
  1382.     int y) /* The global y-coordinate */
  1383. {
  1384.     MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl;
  1385.     long popUpResult;
  1386.     int result;
  1387.     if (inPostMenu > 0) {
  1388. Tcl_AppendResult(interp,
  1389. "Cannot call post menu while already posting menu", NULL);
  1390. result = TCL_ERROR;
  1391.     } else {
  1392. short menuID;
  1393. Window window;
  1394. int oldWidth = menuPtr->totalWidth;
  1395. inPostMenu++;
  1396. result = TkPreprocessMenu(menuPtr);
  1397. /*
  1398.  * The post commands could have deleted the menu, which means
  1399.  * we are dead and should go away.
  1400.  */
  1401. if (result != TCL_OK || !menuPtr->tkwin) {
  1402.     goto endPostMenu;
  1403. }
  1404. CompleteIdlers(menuPtr);
  1405. if (menuBarFlags & MENUBAR_REDRAW_PENDING) {
  1406.     Tcl_CancelIdleCall(DrawMenuBarWhenIdle, NULL);
  1407.     DrawMenuBarWhenIdle(NULL);
  1408. }
  1409. RecursivelyInsertMenu(menuPtr);
  1410. TkMacOSXTrackingLoop(1);
  1411. popUpResult = PopUpMenuSelect(macMenuHdl, y, x, menuPtr->active);
  1412. TkMacOSXTrackingLoop(0);
  1413. menuPtr->totalWidth = oldWidth;
  1414. /*
  1415.  * Simulate the mouse up.
  1416.  */
  1417. window = Tk_WindowId(menuPtr->tkwin);
  1418. TkGenerateButtonEventForXPointer(window);
  1419. /*
  1420.  * Dispatch the command.
  1421.  */
  1422. menuID = HiWord(popUpResult);
  1423. if (menuID != 0) {
  1424.     result = TkMacOSXDispatchMenuEvent(menuID, LoWord(popUpResult));
  1425. }
  1426. endPostMenu:
  1427. inPostMenu--;
  1428.     }
  1429.     return result;
  1430. }
  1431. /*
  1432.  *----------------------------------------------------------------------
  1433.  *
  1434.  * TkpMenuNewEntry --
  1435.  *
  1436.  * Adds a pointer to a new menu entry structure with the platform-
  1437.  * specific fields filled in. The Macintosh uses the
  1438.  * platformEntryData field of the TkMenuEntry record to store
  1439.  * geometry information.
  1440.  *
  1441.  * Results:
  1442.  * Standard TCL error.
  1443.  *
  1444.  * Side effects:
  1445.  * Storage gets allocated. New menu entry data is put into the
  1446.  * platformEntryData field of the mePtr.
  1447.  *
  1448.  *----------------------------------------------------------------------
  1449.  */
  1450. int
  1451. TkpMenuNewEntry(
  1452.     TkMenuEntry *mePtr) /* The menu we are adding an entry to */
  1453. {
  1454.     EntryGeometry *geometryPtr =
  1455.     (EntryGeometry *) ckalloc(sizeof(EntryGeometry));
  1456.     TkMenu *menuPtr = mePtr->menuPtr;
  1457.     geometryPtr->accelTextStart = 0;
  1458.     geometryPtr->accelTextWidth = 0;
  1459.     geometryPtr->nonAccelMargin = 0;
  1460.     geometryPtr->modifierWidth = 0;
  1461.     geometryPtr->modifierNum = 0;
  1462.     geometryPtr->accelGlyph = 0;
  1463.     mePtr->platformEntryData = (TkMenuPlatformEntryData) geometryPtr;
  1464.     if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
  1465. menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
  1466. Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr);
  1467.     }
  1468.     return TCL_OK;
  1469. }
  1470. /*
  1471.  *----------------------------------------------------------------------
  1472.  *
  1473.  * Tk_MacOSXTurnOffMenus --
  1474.  *
  1475.  * Turns off all the menu drawing code. This is more than just disabling
  1476.  * the "menu" command, this means that Tk will NEVER touch the menubar.
  1477.  * It is needed in the Plugin, where Tk does not own the menubar.
  1478.  *
  1479.  * Results:
  1480.  * None.
  1481.  *
  1482.  * Side effects:
  1483.  * A flag is set which will disable all menu drawing.
  1484.  *
  1485.  *----------------------------------------------------------------------
  1486.  */
  1487. void
  1488. Tk_MacOSXTurnOffMenus(void)
  1489. {
  1490.     gNoTkMenus = 1;
  1491. }
  1492. /*
  1493.  *----------------------------------------------------------------------
  1494.  *
  1495.  * DrawMenuBarWhenIdle --
  1496.  *
  1497.  * Update the menu bar next time there is an idle event.
  1498.  *
  1499.  * Results:
  1500.  * None.
  1501.  *
  1502.  * Side effects:
  1503.  * Menu bar is redrawn.
  1504.  *
  1505.  *----------------------------------------------------------------------
  1506.  */
  1507. void
  1508. DrawMenuBarWhenIdle(
  1509.     ClientData clientData) /* ignored here */
  1510. {
  1511.     TkMenuReferences *menuRefPtr;
  1512.     TkMenu *appleMenuPtr, *helpMenuPtr, *menuBarPtr = NULL;
  1513.     MenuHandle macMenuHdl;
  1514.     Tcl_HashEntry *hashEntryPtr;
  1515.     /*
  1516.      * If we have been turned off, exit.
  1517.      */
  1518.     if (gNoTkMenus) {
  1519. return;
  1520.     }
  1521.     /*
  1522.      * We need to clear the apple and help menus of any extra items.
  1523.      */
  1524.     if (currentAppleMenuID != 0) {
  1525. hashEntryPtr = Tcl_FindHashEntry(&commandTable,
  1526. (char*)(intptr_t)currentAppleMenuID);
  1527. appleMenuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
  1528. TkpDestroyMenu(appleMenuPtr);
  1529. TkpNewMenu(appleMenuPtr);
  1530. appleMenuPtr->menuFlags &= ~MENU_APPLE_MENU;
  1531. appleMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
  1532. Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) appleMenuPtr);
  1533.     }
  1534.     if (currentHelpMenuID != 0) {
  1535. hashEntryPtr = Tcl_FindHashEntry(&commandTable,
  1536. (char*)(intptr_t)currentHelpMenuID);
  1537. helpMenuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
  1538. TkpDestroyMenu(helpMenuPtr);
  1539. TkpNewMenu(helpMenuPtr);
  1540. helpMenuPtr->menuFlags &= ~MENU_HELP_MENU;
  1541. helpMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
  1542. Tcl_DoWhenIdle(ReconfigureMacintoshMenu,
  1543. (ClientData) helpMenuPtr);
  1544.     }
  1545.     /*
  1546.      * We need to find the clone of this menu that is the menubar.
  1547.      * Once we do that, for every cascade in the menu, we need to
  1548.      * insert the Mac menu in the Mac menubar. Finally, we need
  1549.      * to redraw the menubar.
  1550.      */
  1551.     menuRefPtr = NULL;
  1552.     if (currentMenuBarName != NULL) {
  1553. menuRefPtr = TkFindMenuReferences(currentMenuBarInterp,
  1554. currentMenuBarName);
  1555.     }
  1556.     if (menuRefPtr) {
  1557. TkMenu *menuPtr;
  1558. TkMenu *cascadeMenuPtr;
  1559. char *appleMenuName, *helpMenuName;
  1560. int appleIndex = -1, helpIndex = -1, i;
  1561. menuPtr = menuRefPtr->menuPtr;
  1562. if (menuPtr != NULL) {
  1563.     TkMenuReferences *specialMenuRefPtr;
  1564.     TkMenuEntry *specialEntryPtr;
  1565.     appleMenuName = ckalloc(strlen(currentMenuBarName) + 1 +
  1566.     strlen(".apple") + 1);
  1567.     sprintf(appleMenuName, "%s.apple", Tk_PathName(menuPtr->tkwin));
  1568.     specialMenuRefPtr = TkFindMenuReferences(currentMenuBarInterp,
  1569.     appleMenuName);
  1570.     if ((specialMenuRefPtr != NULL)
  1571.     && (specialMenuRefPtr->menuPtr != NULL)) {
  1572. for (specialEntryPtr = specialMenuRefPtr->parentEntryPtr;
  1573. specialEntryPtr != NULL;
  1574. specialEntryPtr = specialEntryPtr->nextCascadePtr) {
  1575.     if (specialEntryPtr->menuPtr == menuPtr) {
  1576. appleIndex = specialEntryPtr->index;
  1577. break;
  1578.     }
  1579. }
  1580.     }
  1581.     ckfree(appleMenuName);
  1582.     helpMenuName = ckalloc(strlen(currentMenuBarName) + 1 +
  1583.     strlen(".help") + 1);
  1584.     sprintf(helpMenuName, "%s.help", Tk_PathName(menuPtr->tkwin));
  1585.     specialMenuRefPtr = TkFindMenuReferences(currentMenuBarInterp,
  1586.     helpMenuName);
  1587.     if ((specialMenuRefPtr != NULL)
  1588.     && (specialMenuRefPtr->menuPtr != NULL)) {
  1589. for (specialEntryPtr = specialMenuRefPtr->parentEntryPtr;
  1590. specialEntryPtr != NULL;
  1591. specialEntryPtr = specialEntryPtr->nextCascadePtr) {
  1592.     if (specialEntryPtr->menuPtr == menuPtr) {
  1593. helpIndex = specialEntryPtr->index;
  1594. break;
  1595.     }
  1596. }
  1597.     }
  1598.     ckfree(helpMenuName);
  1599. }
  1600. for (menuBarPtr = menuPtr;
  1601. (menuBarPtr != NULL) && (menuBarPtr->menuType != MENUBAR);
  1602. menuBarPtr = menuBarPtr->nextInstancePtr) {
  1603.     /*
  1604.      * Null loop body.
  1605.      */
  1606. }
  1607. if (menuBarPtr) {
  1608.     if (menuBarPtr->tearoff != menuPtr->tearoff) {
  1609. if (menuBarPtr->tearoff) {
  1610.     appleIndex = (-1 == appleIndex) ? appleIndex
  1611.     : appleIndex + 1;
  1612.     helpIndex = (-1 == helpIndex) ? helpIndex
  1613.     : helpIndex + 1;
  1614. } else {
  1615.     appleIndex = (-1 == appleIndex) ? appleIndex
  1616.     : appleIndex - 1;
  1617.     helpIndex = (-1 == helpIndex) ? helpIndex
  1618.     : helpIndex - 1;
  1619. }
  1620.     }
  1621.     ClearMenuBar();
  1622.     if (appleIndex == -1) {
  1623. InsertMenu(tkAppleMenu, 0);
  1624. currentAppleMenuID = 0;
  1625. tkCurrentAppleMenu = tkAppleMenu;
  1626.     } else {
  1627. short appleID;
  1628. appleMenuPtr = menuBarPtr->entries[appleIndex]
  1629. ->childMenuRefPtr->menuPtr;
  1630. TkpDestroyMenu(appleMenuPtr);
  1631. TkMacOSXGetNewMenuID(appleMenuPtr->interp, appleMenuPtr, 0,
  1632. &appleID);
  1633. macMenuHdl = NewMenu(appleID, "p24");
  1634. appleMenuPtr->platformData =
  1635. (TkMenuPlatformData) ckalloc(sizeof(MacMenu));
  1636. ((MacMenu *)appleMenuPtr->platformData)->menuHdl
  1637. = macMenuHdl;
  1638. appleMenuPtr->menuFlags |= MENU_APPLE_MENU;
  1639. if (!(appleMenuPtr->menuFlags
  1640. & MENU_RECONFIGURE_PENDING)) {
  1641.     appleMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
  1642.     Tcl_DoWhenIdle(ReconfigureMacintoshMenu,
  1643.     (ClientData) appleMenuPtr);
  1644. }
  1645. InsertMenu(macMenuHdl, 0);
  1646. RecursivelyInsertMenu(appleMenuPtr);
  1647. currentAppleMenuID = appleID;
  1648. tkCurrentAppleMenu = macMenuHdl;
  1649.     }
  1650.     if (helpIndex == -1) {
  1651. currentHelpMenuID = 0;
  1652.     }
  1653.     for (i = 0; i < menuBarPtr->numEntries; i++) {
  1654. if (i == appleIndex) {
  1655.     if (menuBarPtr->entries[i]->state == ENTRY_DISABLED) {
  1656. DisableMenuItem(((MacMenu *) menuBarPtr->entries[i]
  1657. ->childMenuRefPtr->menuPtr
  1658. ->platformData)->menuHdl, 0);
  1659.     } else {
  1660. EnableMenuItem(((MacMenu *) menuBarPtr->entries[i]
  1661. ->childMenuRefPtr->menuPtr
  1662. ->platformData)->menuHdl, 0);
  1663.     }
  1664.     continue;
  1665. } else if (i == helpIndex) {
  1666.     TkMenu *helpMenuPtr = menuBarPtr->entries[i]
  1667.     ->childMenuRefPtr->menuPtr;
  1668.     if (helpMenuPtr == NULL) {
  1669. continue;
  1670.     }
  1671.     helpMenuPtr->menuFlags |= MENU_HELP_MENU;
  1672.     if (!(helpMenuPtr->menuFlags
  1673.     & MENU_RECONFIGURE_PENDING)) {
  1674. helpMenuPtr->menuFlags
  1675. |= MENU_RECONFIGURE_PENDING;
  1676. Tcl_DoWhenIdle(ReconfigureMacintoshMenu,
  1677. (ClientData) helpMenuPtr);
  1678.     }
  1679.     macMenuHdl =
  1680.     ((MacMenu *) helpMenuPtr->platformData)->menuHdl;
  1681.     currentHelpMenuID = GetMenuID(macMenuHdl);
  1682. } else if (menuBarPtr->entries[i]->type
  1683. == CASCADE_ENTRY) {
  1684.     if ((menuBarPtr->entries[i]->childMenuRefPtr != NULL)
  1685.     && menuBarPtr->entries[i]->childMenuRefPtr
  1686.     ->menuPtr != NULL) {
  1687. cascadeMenuPtr = menuBarPtr->entries[i]
  1688. ->childMenuRefPtr->menuPtr;
  1689. macMenuHdl = ((MacMenu *) cascadeMenuPtr
  1690. ->platformData)->menuHdl;
  1691. DeleteMenu(GetMenuID(macMenuHdl));
  1692. InsertMenu(macMenuHdl, 0);
  1693. RecursivelyInsertMenu(cascadeMenuPtr);
  1694. if (menuBarPtr->entries[i]->state == ENTRY_DISABLED) {
  1695.     DisableMenuItem(((MacMenu *) menuBarPtr->entries[i]
  1696.     ->childMenuRefPtr->menuPtr
  1697.     ->platformData)->menuHdl, 0);
  1698. } else {
  1699.     EnableMenuItem(((MacMenu *) menuBarPtr->entries[i]
  1700.     ->childMenuRefPtr->menuPtr
  1701.     ->platformData)->menuHdl, 0);
  1702.  }
  1703.     }
  1704. }
  1705.     }
  1706. }
  1707.     }
  1708.     if (!menuRefPtr || !menuBarPtr) {
  1709. SetDefaultMenubar();
  1710.     }
  1711.     DrawMenuBar();
  1712.     menuBarFlags &= ~MENUBAR_REDRAW_PENDING;
  1713. }
  1714. /*
  1715.  *----------------------------------------------------------------------
  1716.  *
  1717.  * RecursivelyInsertMenu --
  1718.  *
  1719.  * Puts all of the cascades of this menu in the Mac hierarchical list.
  1720.  *
  1721.  * Results:
  1722.  * None.
  1723.  *
  1724.  * Side effects:
  1725.  * The menubar is changed.
  1726.  *
  1727.  *----------------------------------------------------------------------
  1728.  */
  1729. void
  1730. RecursivelyInsertMenu(
  1731.     TkMenu *menuPtr) /* All of the cascade items in this menu
  1732.  * will be inserted into the mac menubar. */
  1733. {
  1734.     int i;
  1735.     TkMenu *cascadeMenuPtr;
  1736.     MenuHandle macMenuHdl;
  1737.     for (i = 0; i < menuPtr->numEntries; i++) {
  1738. if (menuPtr->entries[i]->type == CASCADE_ENTRY) {
  1739.     if ((menuPtr->entries[i]->childMenuRefPtr != NULL) &&
  1740.     (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) {
  1741. cascadeMenuPtr = menuPtr->entries[i]->childMenuRefPtr->menuPtr;
  1742. macMenuHdl =
  1743. ((MacMenu *) cascadeMenuPtr->platformData)->menuHdl;
  1744. InsertMenu(macMenuHdl, -1);
  1745. RecursivelyInsertMenu(cascadeMenuPtr);
  1746.     }
  1747. }
  1748.     }
  1749. }
  1750. /*
  1751.  *----------------------------------------------------------------------
  1752.  *
  1753.  * RecursivelyDeleteMenu --
  1754.  *
  1755.  * Takes all of the cascades of this menu out of the Mac hierarchical
  1756.  * list.
  1757.  *
  1758.  * Results:
  1759.  * None.
  1760.  *
  1761.  * Side effects:
  1762.  * The menubar is changed.
  1763.  *
  1764.  *----------------------------------------------------------------------
  1765.  */
  1766. void
  1767. RecursivelyDeleteMenu(
  1768.     TkMenu *menuPtr) /* All of the cascade items in this menu
  1769.  * will be deleted from the mac menubar. */
  1770. {
  1771.     int i;
  1772.     TkMenu *cascadeMenuPtr;
  1773.     MenuHandle macMenuHdl;
  1774.     for (i = 0; i < menuPtr->numEntries; i++) {
  1775. if (menuPtr->entries[i]->type == CASCADE_ENTRY) {
  1776.     if ((menuPtr->entries[i]->childMenuRefPtr != NULL) &&
  1777.     (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) {
  1778. cascadeMenuPtr = menuPtr->entries[i]->childMenuRefPtr->menuPtr;
  1779. macMenuHdl =
  1780. ((MacMenu *) cascadeMenuPtr->platformData)->menuHdl;
  1781. DeleteMenu(GetMenuID(macMenuHdl));
  1782. RecursivelyDeleteMenu(cascadeMenuPtr);
  1783.     }
  1784. }
  1785.     }
  1786. }
  1787. /*
  1788.  *----------------------------------------------------------------------
  1789.  *
  1790.  * SetDefaultMenubar --
  1791.  *
  1792.  * Puts the Apple, File and Edit menus into the Macintosh menubar.
  1793.  *
  1794.  * Results:
  1795.  * None.
  1796.  *
  1797.  * Side effects:
  1798.  * The menubar is changed.
  1799.  *
  1800.  *----------------------------------------------------------------------
  1801.  */
  1802. void
  1803. SetDefaultMenubar(void)
  1804. {
  1805.     if (currentMenuBarName != NULL) {
  1806. ckfree(currentMenuBarName);
  1807. currentMenuBarName = NULL;
  1808.     }
  1809.     currentMenuBarOwner = NULL;
  1810.     ClearMenuBar();
  1811.     InsertMenu(tkAppleMenu, 0);
  1812.     InsertMenu(tkFileMenu, 0);
  1813.     InsertMenu(tkEditMenu, 0);
  1814.     if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) {
  1815. Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL);
  1816. menuBarFlags |= MENUBAR_REDRAW_PENDING;
  1817.     }
  1818. }
  1819. /*
  1820.  *----------------------------------------------------------------------
  1821.  *
  1822.  * TkpSetMainMenubar --
  1823.  *
  1824.  * Puts the menu associated with a window into the menubar. Should
  1825.  * only be called when the window is in front.
  1826.  *
  1827.  * Results:
  1828.  * None.
  1829.  *
  1830.  * Side effects:
  1831.  * The menubar is changed.
  1832.  *
  1833.  *----------------------------------------------------------------------
  1834.  */
  1835. void
  1836. TkpSetMainMenubar(
  1837.     Tcl_Interp *interp, /* The interpreter of the application */
  1838.     Tk_Window tkwin, /* The frame we are setting up */
  1839.     char *menuName) /* The name of the menu to put in front.
  1840.  * If NULL, use the default menu bar.
  1841.  */
  1842. {
  1843.     TkWindow *winPtr = (TkWindow *) tkwin;
  1844.     WindowRef macWindowPtr;
  1845.     WindowRef frontNonFloating;
  1846.     macWindowPtr = TkMacOSXDrawableWindow(winPtr->window);
  1847.     frontNonFloating = ActiveNonFloatingWindow();
  1848.     if ((macWindowPtr == NULL) || (macWindowPtr != frontNonFloating)) {
  1849. return;
  1850.     }
  1851.     if ((currentMenuBarInterp != interp) || (currentMenuBarOwner != tkwin)
  1852.     || (currentMenuBarName == NULL) || (menuName == NULL)
  1853.     || (strcmp(menuName, currentMenuBarName) != 0)) {
  1854. Tk_Window searchWindow;
  1855. TopLevelMenubarList *listPtr;
  1856. if (currentMenuBarName != NULL) {
  1857.     ckfree(currentMenuBarName);
  1858. }
  1859. if (menuName == NULL) {
  1860.     searchWindow = tkwin;
  1861.     if (strcmp(Tk_Class(searchWindow), "Menu") == 0) {
  1862. TkMenuReferences *menuRefPtr;
  1863. menuRefPtr = TkFindMenuReferences(interp, Tk_PathName(tkwin));
  1864. if (menuRefPtr != NULL) {
  1865.     TkMenu *menuPtr = menuRefPtr->menuPtr;
  1866.     if (menuPtr != NULL) {
  1867. searchWindow = menuPtr->masterMenuPtr->tkwin;
  1868.     }
  1869. }
  1870.     }
  1871.     for (; searchWindow != NULL;
  1872.     searchWindow = Tk_Parent(searchWindow)) {
  1873. for (listPtr = windowListPtr; listPtr != NULL;
  1874. listPtr = listPtr->nextPtr) {
  1875.     if (listPtr->tkwin == searchWindow) {
  1876. break;
  1877.     }
  1878. }
  1879. if (listPtr != NULL) {
  1880.     menuName = Tk_PathName(
  1881.     listPtr->menuPtr->masterMenuPtr->tkwin);
  1882.     break;
  1883. }
  1884.     }
  1885. }
  1886. if (menuName == NULL) {
  1887.     currentMenuBarName = NULL;
  1888. } else {
  1889.     currentMenuBarName = ckalloc(strlen(menuName) + 1);
  1890.     strcpy(currentMenuBarName, menuName);
  1891. }
  1892. currentMenuBarOwner = tkwin;
  1893. currentMenuBarInterp = interp;
  1894.     }
  1895.     if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) {
  1896. Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL);
  1897. menuBarFlags |= MENUBAR_REDRAW_PENDING;
  1898.     }
  1899. }
  1900. /*
  1901.  *----------------------------------------------------------------------
  1902.  *
  1903.  * TkpSetWindowMenuBar --
  1904.  *
  1905.  * Associates a given menu with a window.
  1906.  *
  1907.  * Results:
  1908.  * None.
  1909.  *
  1910.  * Side effects:
  1911.  * On Windows and UNIX, associates the platform menu with the
  1912.  * platform window.
  1913.  *
  1914.  *----------------------------------------------------------------------
  1915.  */
  1916. void
  1917. TkpSetWindowMenuBar(
  1918.     Tk_Window tkwin, /* The window we are setting the menu in */
  1919.     TkMenu *menuPtr) /* The menu we are setting */
  1920. {
  1921.     TopLevelMenubarList *listPtr, *prevPtr;
  1922.     /*
  1923.      * Remove any existing reference to this window.
  1924.      */
  1925.     for (prevPtr = NULL, listPtr = windowListPtr;
  1926.     listPtr != NULL;
  1927.     prevPtr = listPtr, listPtr = listPtr->nextPtr) {
  1928. if (listPtr->tkwin == tkwin) {
  1929.     break;
  1930. }
  1931.     }
  1932.     if (listPtr != NULL) {
  1933. if (prevPtr != NULL) {
  1934.     prevPtr->nextPtr = listPtr->nextPtr;
  1935. } else {
  1936.     windowListPtr = listPtr->nextPtr;
  1937. }
  1938. ckfree((char *) listPtr);
  1939.     }
  1940.     if (menuPtr != NULL) {
  1941. listPtr = (TopLevelMenubarList *) ckalloc(sizeof(TopLevelMenubarList));
  1942. listPtr->nextPtr = windowListPtr;
  1943. windowListPtr = listPtr;
  1944. listPtr->tkwin = tkwin;
  1945. listPtr->menuPtr = menuPtr;
  1946.     }
  1947. }
  1948. /*
  1949.  *----------------------------------------------------------------------
  1950.  *
  1951.  * EventuallyInvokeMenu --
  1952.  *
  1953.  * This IdleTime callback actually invokes the menu command
  1954.  * scheduled in TkMacOSXDispatchMenuEvent.
  1955.  *
  1956.  * Results:
  1957.  * None.
  1958.  *
  1959.  * Side effects:
  1960.  * Commands get executed.
  1961.  *
  1962.  *----------------------------------------------------------------------
  1963.  */
  1964. void
  1965. EventuallyInvokeMenu (
  1966.     ClientData data)
  1967. {
  1968.     struct MenuCommandHandlerData *realData =
  1969.     (struct MenuCommandHandlerData *) data;
  1970.     int code;
  1971.     code = TkInvokeMenu(realData->menuPtr->interp, realData->menuPtr,
  1972.     realData->index);
  1973.     if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) {
  1974. Tcl_AddErrorInfo(realData->menuPtr->interp, "n    (menu invoke)");
  1975. Tcl_BackgroundError(realData->menuPtr->interp);
  1976.     }
  1977.     if (realData->menuPtr->tkwin) {
  1978. RecursivelyClearActiveMenu(realData->menuPtr);
  1979.     }
  1980.     TkMacOSXClearMenubarActive();
  1981.     Tcl_Release(realData->menuPtr->interp);
  1982.     Tcl_Release(realData->menuPtr);
  1983. }
  1984. /*
  1985.  *----------------------------------------------------------------------
  1986.  *
  1987.  * TkMacOSXDispatchMenuEvent --
  1988.  *
  1989.  * Given a menu id and an item, dispatches the command associated
  1990.  * with it.
  1991.  *
  1992.  * Results:
  1993.  * None.
  1994.  *
  1995.  * Side effects:
  1996.  * Commands for the event are scheduled for execution at idle time.
  1997.  *
  1998.  *----------------------------------------------------------------------
  1999.  */
  2000. int
  2001. TkMacOSXDispatchMenuEvent(
  2002.     int menuID, /* The menu id of the menu we are invoking */
  2003.     int index) /* The one-based index of the item that was
  2004.  * selected. */
  2005. {
  2006.     int result = TCL_OK;
  2007.     if (menuID != 0) {
  2008. if (menuID == kHMHelpMenuID) {
  2009.     if (currentMenuBarOwner != NULL) {
  2010. TkMenuReferences *helpMenuRef;
  2011. char *helpMenuName = ckalloc(strlen(currentMenuBarName)
  2012. + strlen(".help") + 1);
  2013. sprintf(helpMenuName, "%s.help", currentMenuBarName);
  2014. helpMenuRef = TkFindMenuReferences(currentMenuBarInterp,
  2015. helpMenuName);
  2016. ckfree(helpMenuName);
  2017. if ((helpMenuRef != NULL) && (helpMenuRef->menuPtr != NULL)) {
  2018.     MenuRef outHelpMenu;
  2019.     MenuItemIndex itemIndex;
  2020.     int newIndex;
  2021.     HMGetHelpMenu(&outHelpMenu, &itemIndex);
  2022.     newIndex = index - itemIndex;
  2023.     result = TkInvokeMenu(currentMenuBarInterp,
  2024.     helpMenuRef->menuPtr, newIndex);
  2025. }
  2026.     }
  2027. } else {
  2028.     Tcl_HashEntry *commandEntryPtr =
  2029.     Tcl_FindHashEntry(&commandTable, (char*)(intptr_t)menuID);
  2030.     if (commandEntryPtr != NULL) {
  2031. TkMenu *menuPtr = (TkMenu *) Tcl_GetHashValue(commandEntryPtr);
  2032. if ((currentAppleMenuID == menuID)
  2033. && (index > menuPtr->numEntries + 1)) {
  2034.     /*
  2035.      * We don't need to do anything here, the standard
  2036.      * Application event handler will open the built-in
  2037.      * Apple menu item for us.
  2038.      */
  2039.     result = TCL_OK;
  2040. } else {
  2041.     struct MenuCommandHandlerData *data
  2042.     = (struct MenuCommandHandlerData *)
  2043.     ckalloc(sizeof(struct MenuCommandHandlerData));
  2044.     Tcl_Preserve(menuPtr->interp);
  2045.     Tcl_Preserve(menuPtr);
  2046.     data->menuPtr = menuPtr;
  2047.     data->index = index - 1;
  2048.     Tcl_DoWhenIdle(EventuallyInvokeMenu,
  2049.     (ClientData) data);
  2050.     /* result = TkInvokeMenu(menuPtr->interp, menuPtr, index - 1); */
  2051. }
  2052.     } else {
  2053. return TCL_ERROR;
  2054.     }
  2055. }
  2056.     }
  2057.     return result;
  2058. }
  2059. /*
  2060.  *----------------------------------------------------------------------
  2061.  *
  2062.  * GetMenuIndicatorGeometry --
  2063.  *
  2064.  * Gets the width and height of the indicator area of a menu.
  2065.  *
  2066.  * Results:
  2067.  * widthPtr and heightPtr are set.
  2068.  *
  2069.  * Side effects:
  2070.  * None.
  2071.  *
  2072.  *----------------------------------------------------------------------
  2073.  */
  2074. void
  2075. GetMenuIndicatorGeometry (
  2076.     TkMenu *menuPtr, /* The menu we are drawing */
  2077.     TkMenuEntry *mePtr, /* The entry we are measuring */
  2078.     Tk_Font tkfont, /* Precalculated font */
  2079.     const Tk_FontMetrics *fmPtr,/* Precalculated font metrics */
  2080.     int *widthPtr, /* The resulting width */
  2081.     int *heightPtr) /* The resulting height */
  2082. {
  2083.     *heightPtr = fmPtr->linespace + menuItemExtraHeight;
  2084.     if (IS_THEME_MENU_FONT(tkfont)) {
  2085. *widthPtr = menuMarkColumnWidth;
  2086.     } else {
  2087. const char markChar = FindMarkCharacter(mePtr);
  2088. const char *markUtf = NULL;
  2089. int len;
  2090. len = GetUtfMarkCharacter(markChar, &markUtf);
  2091. *widthPtr = Tk_TextWidth(tkfont, markUtf, len) + 2*menuMarkIndent;
  2092.     }
  2093. }
  2094. /*
  2095.  *----------------------------------------------------------------------
  2096.  *
  2097.  * GetMenuAccelGeometry --
  2098.  *
  2099.  * Gets the width and height of the accelerator area of a menu.
  2100.  *
  2101.  * Results:
  2102.  * widthPtr and heightPtr are set.
  2103.  *
  2104.  * Side effects:
  2105.  * None.
  2106.  *
  2107.  *----------------------------------------------------------------------
  2108.  */
  2109. void
  2110. GetMenuAccelGeometry (
  2111.     TkMenu *menuPtr, /* The menu we are measuring */
  2112.     TkMenuEntry *mePtr, /* The entry we are measuring */
  2113.     Tk_Font tkfont, /* The precalculated font */
  2114.     const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */
  2115.     int *modWidthPtr, /* The width of all of the key
  2116.  * modifier symbols. */
  2117.     int *textWidthPtr, /* The resulting width */
  2118.     int *heightPtr) /* The resulting height */
  2119. {
  2120.     *heightPtr = fmPtr->linespace + menuItemExtraHeight;
  2121.     *modWidthPtr = menuSymbols[COMMAND_SYMBOL].width;
  2122.     *textWidthPtr = 0;
  2123.     if (mePtr->type != CASCADE_ENTRY && mePtr->accelLength > 0) {
  2124. const char *accel = (mePtr->accelPtr == NULL) ? ""
  2125. : Tcl_GetString(mePtr->accelPtr);
  2126. EntryGeometry *geometryPtr = (EntryGeometry*)mePtr->platformEntryData;
  2127. if (IS_THEME_MENU_FONT(tkfont)) {
  2128.     CFStringRef cfStr;
  2129.     int width = 0;
  2130.     int maxWidth = ((TkFont *)tkfont)->fm.maxWidth;
  2131.     if (geometryPtr->accelGlyph) {
  2132. cfStr = CFStringCreateWithBytes(NULL,
  2133. (UInt8*)&geometryPtr->accelGlyph, 1,
  2134. kTextEncodingMacKeyboardGlyphs, false);
  2135. if (cfStr) {
  2136.     width = MeasureThemeText(cfStr, kThemeMenuItemCmdKeyFont);
  2137.     CFRelease(cfStr);
  2138. }
  2139.     }
  2140.     if ((mePtr->entryFlags & ENTRY_ACCEL_MASK) == 0) {
  2141. if (!geometryPtr->accelGlyph) {
  2142.      width = Tk_TextWidth(tkfont, accel, mePtr->accelLength);
  2143.  }
  2144. *textWidthPtr = maxWidth;
  2145. if (width < maxWidth) {
  2146.     *modWidthPtr = 0;
  2147. } else {
  2148.     *modWidthPtr = width - maxWidth;
  2149. }