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
16#include "tkMacOSXPrivate.h"
17#include "tkMenubutton.h"
18#include "tkMenu.h"
19#include "tkColor.h"
20#include "tkFont.h"
21#include "tkMacOSXDebug.h"
22
23/*
24#ifdef TK_MAC_DEBUG
25#define TK_MAC_DEBUG_MENUS
26#endif
27*/
28
29#define USE_TK_MDEF
30
31typedef struct MacMenu {
32    MenuRef menuHdl;		/* The Menu Manager data structure. */
33#ifdef USE_TK_MDEF
34    int useMDEF;		/* true if this menu uses the MDEF */
35#endif
36} MacMenu;
37
38typedef struct MenuEntryUserData {
39    Drawable mdefDrawable;
40    TkMenuEntry *mePtr;
41    Tk_Font tkfont;
42    Tk_FontMetrics *fmPtr;
43} MenuEntryUserData;
44
45/*
46 * Platform specific flags for menu entries
47 *
48 * ENTRY_COMMAND_ACCEL		Indicates the entry has the command key
49 *				in its accelerator string.
50 * ENTRY_OPTION_ACCEL		Indicates the entry has the option key
51 *				in its accelerator string.
52 * ENTRY_SHIFT_ACCEL		Indicates the entry has the shift key
53 *				in its accelerator string.
54 * ENTRY_CONTROL_ACCEL		Indicates the entry has the control key
55 *				in its accelerator string.
56 */
57
58#define ENTRY_COMMAND_ACCEL	ENTRY_PLATFORM_FLAG1
59#define ENTRY_OPTION_ACCEL	ENTRY_PLATFORM_FLAG2
60#define ENTRY_SHIFT_ACCEL	ENTRY_PLATFORM_FLAG3
61#define ENTRY_CONTROL_ACCEL	ENTRY_PLATFORM_FLAG4
62#define ENTRY_ACCEL_MASK	(ENTRY_COMMAND_ACCEL | ENTRY_OPTION_ACCEL \
63				| ENTRY_SHIFT_ACCEL | ENTRY_CONTROL_ACCEL)
64#define MODIFIER_NUM 4
65
66/*
67 * This structure is used to keep track of subfields within Macintosh menu
68 * items.
69 */
70
71typedef struct EntryGeometry {
72    int accelTextStart;		/* Offset into the accel string where
73				 * the text starts. Everything before
74				 * this is modifier key descriptions.
75				 */
76    int modifierWidth;		/* Width of modifier symbols. */
77    int accelTextWidth;		/* Width of the text after the modifier
78				 * keys. */
79    int nonAccelMargin;		/* The width of the margin for entries
80				 * without accelerators. */
81    int modifierNum;		/* Number of modifiers */
82    Tcl_UniChar modifierUniChars[MODIFIER_NUM];
83				/* Modifiers in unicode */
84    char accelGlyph;		/* Accelerator glyph, if any */
85} EntryGeometry;
86
87/*
88 * Structure to keep track of toplevel windows and their menubars.
89 */
90
91typedef struct TopLevelMenubarList {
92    struct TopLevelMenubarList *nextPtr;
93				/* The next window in the list. */
94    Tk_Window tkwin;		/* The toplevel window. */
95    TkMenu *menuPtr;		/* The menu associated with this
96				 * toplevel. */
97} TopLevelMenubarList;
98
99/*
100 * Platform-specific flags for menus.
101 *
102 * MENU_APPLE_MENU		0 indicates a custom Apple menu has
103 *				not been installed; 1 a custom Apple
104 *				menu has been installed.
105 * MENU_HELP_MENU		0 indicates a custom Help menu has
106 *				not been installed; 1 a custom Help
107 *				menu has been installed.
108 * MENU_RECONFIGURE_PENDING	1 indicates that an idle handler has
109 *				been scheduled to reconfigure the
110 *				Macintosh MenuHandle.
111 */
112
113#define MENU_APPLE_MENU			MENU_PLATFORM_FLAG1
114#define MENU_HELP_MENU			MENU_PLATFORM_FLAG2
115#define MENU_RECONFIGURE_PENDING	MENU_PLATFORM_FLAG3
116
117#define CASCADE_CMD (0x1b)	/* The special command char for cascade
118				 * menus. */
119#define MENUBAR_REDRAW_PENDING 1
120
121static int gNoTkMenus = 0;	/* This is used by Tk_MacOSXTurnOffMenus as the
122				 * flag that Tk is not to draw any menus. */
123
124static Tcl_HashTable commandTable;
125				/* The list of menuInstancePtrs associated with
126				 * menu ids */
127static short currentAppleMenuID;
128				/* The id of the current Apple menu. 0 for
129				 * none. */
130static short currentHelpMenuID; /* The id of the current Help menu. 0 for
131				 * none. */
132static Tcl_Interp *currentMenuBarInterp;
133				/* The interpreter of the window that owns
134				 * the current menubar. */
135static char *currentMenuBarName;
136				/* Malloced. Name of current menu in menu bar.
137				 * NULL if no menu set. TO DO: make this a
138				 * DString. */
139static Tk_Window currentMenuBarOwner;
140				/* Which window owns the current menu bar. */
141static int inPostMenu;		/* We cannot be re-entrant like X
142				 * windows. */
143static short lastMenuID;	/* To pass to NewMenu; need to figure out
144				 * a good way to do this. */
145static short lastCascadeID;
146				/* Cascades have to have ids that are
147				 * less than 256. */
148static int menuBarFlags;	/* Used for whether the menu bar needs
149				 * redrawing or not. */
150
151struct MenuCommandHandlerData { /* This is the ClientData we pass to */
152    TkMenu *menuPtr;		/* Tcl_DoWhenIdle to move handling */
153    int index;			/* menu commands to the event loop. */
154};
155
156static TopLevelMenubarList *windowListPtr;
157				/* A list of windows that have menubars set. */
158
159/*
160 * Array of unicode, charcode and utf representations of the most common
161 * special menu symbols.
162 */
163typedef struct MenuSymbol {
164    const Tcl_UniChar unicode;
165    const char charCode;
166    /* char padding; */
167    int utfLen, width;
168    char utf[TCL_UTF_MAX + 1];
169} MenuSymbol;
170
171static MenuSymbol menuSymbols[] = {
172    {kCommandUnicode,	kCommandCharCode},
173    {kOptionUnicode,	kMenuOptionGlyph},
174    {kControlUnicode,	kMenuControlGlyph},
175    {kShiftUnicode,	kMenuShiftGlyph},
176    {kCheckUnicode,	kCheckCharCode},
177    {kDiamondUnicode,	kDiamondCharCode},
178    {kBulletUnicode,	kBulletCharCode},
179    {0x2026,		kNullCharCode},
180    {0,			0},
181};
182
183enum MenuSymbolIdx {
184    COMMAND_SYMBOL,
185    OPTION_SYMBOL,
186    CONTROL_SYMBOL,
187    SHIFT_SYMBOL,
188    CHECK_SYMBOL,
189    DIAMDOND_SYMBOL,
190    BULLET_SYMBOL,
191    ELLIPSIS_SYMBOL,
192};
193
194MenuRef tkCurrentAppleMenu = NULL;
195
196static SInt32 menuMarkColumnWidth = 0, menuMarkIndent = 0;
197static SInt32 menuTextLeadingEdgeMargin = 0, menuTextTrailingEdgeMargin = 0;
198static SInt16 menuItemExtraHeight = 0, menuItemExtraWidth = 0;
199static SInt16 menuSeparatorHeight = 0;
200
201/*
202 * Forward declarations for procedures defined later in this file:
203 */
204
205MODULE_SCOPE int TkMacOSXGetNewMenuID(Tcl_Interp *interp, TkMenu *menuInstPtr,
206	int cascade, short *menuIDPtr);
207MODULE_SCOPE void TkMacOSXFreeMenuID(short menuID);
208
209static void CompleteIdlers(TkMenu *menuPtr);
210static void DrawMenuBarWhenIdle(ClientData clientData);
211static void DrawMenuEntryAccelerator(TkMenu *menuPtr, TkMenuEntry *mePtr,
212	Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr,
213	Tk_3DBorder activeBorder, int x, int y, int width, int height,
214	int drawArrow);
215static void DrawMenuEntryBackground(TkMenu *menuPtr, TkMenuEntry *mePtr,
216	Drawable d, Tk_3DBorder activeBorder, Tk_3DBorder bgBorder, int x,
217	int y, int width, int heigth);
218static void DrawMenuEntryIndicator(TkMenu *menuPtr, TkMenuEntry *mePtr,
219	Drawable d, GC gc, GC indicatorGC, Tk_Font tkfont,
220	const Tk_FontMetrics *fmPtr, int x, int y, int width, int height);
221static void DrawMenuEntryLabel(TkMenu * menuPtr, TkMenuEntry *mePtr,
222	Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x,
223	int y, int width, int height);
224static void DrawMenuSeparator(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d,
225	GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y,
226	int width, int height);
227static void DrawTearoffEntry(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d,
228	GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y,
229	int width, int height);
230static void EventuallyInvokeMenu(ClientData data);
231static void GetEntryText(TkMenuEntry *mePtr, Tcl_DString *dStringPtr);
232static void GetMenuAccelGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr,
233	Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *modWidthPtr,
234	int *textWidthPtr, int *heightPtr);
235static void GetMenuLabelGeometry(TkMenuEntry *mePtr, Tk_Font tkfont,
236	const Tk_FontMetrics *fmPtr, int *widthPtr, int *heightPtr);
237static void GetMenuIndicatorGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr,
238	Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr,
239	int *heightPtr);
240static void GetMenuSeparatorGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr,
241	Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr,
242	int *heightPtr);
243static TkMenuEntry* GetParentMenuEntry(TkMenu *menuPtr);
244static void GetTearoffEntryGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr,
245	Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr,
246	int *heightPtr);
247static char FindMarkCharacter(TkMenuEntry *mePtr);
248static int GetUtfMarkCharacter(char markChar, const char **markUtfPtr);
249static TkMenu* MenuPtrForMenuRef(MenuRef menu);
250static int ParseAccelerators(const char **accelStringPtr, int *modifierNumPtr,
251	Tcl_UniChar *modifierUniChars, int *modifierWidth);
252static void MenuSelectEvent(TkMenu *menuPtr);
253static void ReconfigureIndividualMenu(TkMenu *menuPtr, MenuHandle macMenuHdl,
254	int base);
255static void ReconfigureMacintoshMenu(ClientData clientData);
256static void RecursivelyClearActiveMenu(TkMenu *menuPtr);
257static void RecursivelyDeleteMenu(TkMenu *menuPtr);
258static void RecursivelyInsertMenu(TkMenu *menuPtr);
259static void SetDefaultMenubar(void);
260static int SetMenuCascade(TkMenu *menuPtr);
261
262#ifdef USE_TK_MDEF
263#define SCREEN_MARGIN 5
264static MacDrawable macMDEFDrawable;
265				/* Drawable for use by MDEF code */
266static int MDEFScrollFlag = 0;	/* Used so that popups don't scroll too soon.*/
267static MenuItemDrawingUPP tkThemeMenuItemDrawingUPP;
268				/* Points to the UPP for theme Item drawing. */
269static Tcl_Obj *useMDEFVar;
270
271static void  DrawMenuBackground(TkMenu *menuPtr, Rect *menuRectPtr,
272	Drawable d);
273static void MenuDefProc(short message, MenuHandle menu, Rect *menuRectPtr,
274	Point hitPt, short *whichItem );
275static void HandleMenuHiliteMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt,
276	SInt16 *whichItem, TkMenu *menuPtr);
277static void HandleMenuDrawMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt,
278	SInt16 *whichItem, TkMenu *menuPtr);
279static void HandleMenuFindItemMsg(MenuRef menu, Rect *menuRectPtr,
280	Point hitPt, SInt16 *whichItem, TkMenu *menuPtr);
281static void HandleMenuPopUpMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt,
282	SInt16 *whichItem, TkMenu *menuPtr);
283static void HandleMenuCalcItemMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt,
284	SInt16 *whichItem, TkMenu *menuPtr);
285static void AppearanceEntryDrawWrapper(TkMenuEntry *mePtr, Rect * menuRectPtr,
286	MenuTrackingData *mtdPtr, Drawable d, Tk_FontMetrics *fmPtr,
287	Tk_Font tkfont, int erase);
288static pascal void ThemeMenuItemDrawingProc(const Rect *inBounds,
289	SInt16 inDepth, Boolean inIsColorDevice, SInt32 inUserData);
290#else /* USE_TK_MDEF */
291#   define useMDEF 0
292#endif /* USE_TK_MDEF */
293
294#define IS_THEME_MENU_FONT(tkfont) (strcmp(Tk_NameOfFont(tkfont), "menu") == 0)
295
296
297/*
298 *----------------------------------------------------------------------
299 *
300 * DrawThemeText --
301 *
302 *	Wrapper for DrawThemeTextBox API.
303 *
304 * Results:
305 *	None.
306 *
307 * Side effects:
308 *	None.
309 *
310 *----------------------------------------------------------------------
311 */
312
313static void
314DrawThemeText(
315    Drawable d,
316    GC gc,
317    CFStringRef string,
318    ThemeFontID font,
319    ThemeDrawState drawState,
320    const Rect* bounds,
321    int baseline,
322    int just)
323{
324    TkMacOSXDrawingContext dc;
325    Rect adjustedBounds;
326
327    /*
328     * Menu item text drawn with the .Keyboard font (used for
329     * kThemeMenuItemCmdKeyFont) won't always have the same ascent and
330     * baseline as text drawn with the regular menu item font, since the
331     * glyphs in the .Keyboard font may have a different height. Therefore, we
332     * first determine the baseline of the text and then adjust the bounds
333     * rect so the baseline aligns with the overall baseline of the menu item.
334     */
335    if (font == kThemeMenuItemCmdKeyFont) {
336	Point size;
337	SInt16 cmdKeyBaseline;
338
339	GetThemeTextDimensions(string, font, drawState, false, &size,
340		&cmdKeyBaseline);
341	adjustedBounds = *bounds;
342	OffsetRect(&adjustedBounds, 0, baseline - bounds->top - size.v -
343		cmdKeyBaseline);
344	bounds = &adjustedBounds;
345    }
346    if (TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) {
347	ChkErr(DrawThemeTextBox, string, font, drawState, false, bounds, just,
348		dc.context);
349	TkMacOSXRestoreDrawingContext(&dc);
350    }
351}
352
353/*
354 *----------------------------------------------------------------------
355 *
356 * MeasureThemeText --
357 *
358 *	Wrapper for GetThemeTextDimensions API.
359 *
360 * Results:
361 *	None.
362 *
363 * Side effects:
364 *	None.
365 *
366 *----------------------------------------------------------------------
367 */
368
369static int
370MeasureThemeText(
371    CFStringRef string,
372    ThemeFontID font)
373{
374    Point pt;
375
376    ChkErr(GetThemeTextDimensions, string, font, kThemeStateActive, false, &pt,
377	NULL);
378    return pt.h;
379}
380
381/*
382 *----------------------------------------------------------------------
383 *
384 * TkMacOSXUseID --
385 *
386 *	Take the ID out of the available list for new menus. Used by the
387 *	default menu bar's menus so that they do not get created at the tk
388 *	level. See TkMacOSXGetNewMenuID for more information.
389 *
390 * Results:
391 *	Returns TCL_OK if the id was not in use. Returns TCL_ERROR if the
392 *	id was in use.
393 *
394 * Side effects:
395 *	A hash table entry in the command table is created with a NULL
396 *	value.
397 *
398 *----------------------------------------------------------------------
399 */
400
401int
402TkMacOSXUseMenuID(
403    short macID)		/* The id to take out of the table */
404{
405    Tcl_HashEntry *commandEntryPtr;
406    int newEntry;
407    int iMacID = macID; /* Do this to remove compiler warning */
408
409    TkMenuInit();
410    commandEntryPtr = Tcl_CreateHashEntry(&commandTable, (char *) iMacID,
411	    &newEntry);
412    if (!newEntry) {
413	return TCL_ERROR;
414    }
415    Tcl_SetHashValue(commandEntryPtr, NULL);
416    return TCL_OK;
417}
418
419/*
420 *----------------------------------------------------------------------
421 *
422 * TkMacOSXGetNewMenuID --
423 *
424 *	Allocates a new menu id and marks it in use. Each menu on the
425 *	mac must be designated by a unique id, which is a short. In
426 *	addition, some ids are reserved by the system. Since Tk uses
427 *	mostly dynamic menus, we must allocate and free these ids on
428 *	the fly. We use the id as a key into a hash table; if there
429 *	is no hash entry, we know that we can use the id.
430 *
431 *	Carbon allows a much larger number of menus than the old APIs.
432 *	I believe this is 32768, but am not sure. This code just uses
433 *	2000 as the upper limit. Unfortunately tk leaks menus when
434 *	cloning, under some circumstances (see bug on sourceforge).
435 *
436 * Results:
437 *	Returns TCL_OK if succesful; TCL_ERROR if there are no more
438 *	ids of the appropriate type to allocate. menuIDPtr contains
439 *	the new id if succesful.
440 *
441 * Side effects:
442 *	An entry is created for the menu in the command hash table,
443 *	and the hash entry is stored in the appropriate field in the
444 *	menu data structure.
445 *
446 *----------------------------------------------------------------------
447 */
448
449int
450TkMacOSXGetNewMenuID(
451    Tcl_Interp *interp,		/* Used for error reporting */
452    TkMenu *menuPtr,		/* The menu we are working with */
453    int cascade,		/* 0 if we are working with a normal menu;
454				 * 1 if we are working with a cascade */
455    short *menuIDPtr)		/* The resulting id */
456{
457    int found = 0;
458    int newEntry;
459    Tcl_HashEntry *commandEntryPtr = NULL;
460    short returnID = *menuIDPtr;
461
462    /*
463     * The following code relies on shorts and unsigned chars wrapping
464     * when the highest value is incremented. Also, the values between
465     * 236 and 255 inclusive are reserved for DA's by the Mac OS.
466     */
467
468    if (!cascade) {
469	short curID = lastMenuID + 1;
470
471	if (curID == 236) {
472	    curID = 256;
473	}
474
475	while (curID != lastMenuID) {
476	    int iCurID = curID;
477	    commandEntryPtr = Tcl_CreateHashEntry(&commandTable,
478		    (char *) iCurID, &newEntry);
479	    if (newEntry == 1) {
480		found = 1;
481		lastMenuID = returnID = curID;
482		break;
483	    }
484	    curID++;
485	    if (curID == 236) {
486		curID = 256;
487	    }
488	}
489    } else {
490	/*
491	 * Cascade ids must be between 0 and 235 only, so they must be
492	 * dealt with separately.
493	 */
494
495	short curID = lastCascadeID + 1;
496
497	if (curID == 2000) {
498	    curID = 0;
499	}
500
501	while (curID != lastCascadeID) {
502	    int iCurID = curID;
503	    commandEntryPtr = Tcl_CreateHashEntry(&commandTable,
504		    (char *) iCurID, &newEntry);
505	    if (newEntry == 1) {
506		found = 1;
507		lastCascadeID = returnID = curID;
508		break;
509	    }
510	    curID++;
511	    if (curID == 2000) {
512		curID = 0;
513	    }
514	}
515    }
516
517    if (!found) {
518	Tcl_ResetResult(interp);
519	Tcl_AppendResult(interp, "No more menus can be allocated.", NULL);
520	return TCL_ERROR;
521    }
522    Tcl_SetHashValue(commandEntryPtr, (char *) menuPtr);
523    *menuIDPtr = returnID;
524    return TCL_OK;
525}
526
527/*
528 *----------------------------------------------------------------------
529 *
530 * TkMacOSXFreeMenuID --
531 *
532 *	Marks the id as free.
533 *
534 * Results:
535 *	None.
536 *
537 * Side effects:
538 *	The hash table entry for the ID is cleared.
539 *
540 *----------------------------------------------------------------------
541 */
542
543void
544TkMacOSXFreeMenuID(
545    short menuID)		/* The id to free */
546{
547    Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&commandTable,
548	    (char*)(intptr_t)menuID);
549
550    if (entryPtr != NULL) {
551	 Tcl_DeleteHashEntry(entryPtr);
552    }
553    if (menuID == currentAppleMenuID) {
554	currentAppleMenuID = 0;
555    }
556    if (menuID == currentHelpMenuID) {
557	currentHelpMenuID = 0;
558    }
559}
560
561/*
562 *----------------------------------------------------------------------
563 *
564 * MenuPtrForMenuRef --
565 *
566 *	Returns a pointer to the TkMenu corresponding to a given
567 *	Carbon MenuRef.
568 *
569 * Results:
570 *	Returns a pointer to a TkMenu or NULL.
571 *
572 * Side effects:
573 *	None.
574 *
575 *----------------------------------------------------------------------
576 */
577
578TkMenu*
579MenuPtrForMenuRef(
580    MenuRef menu)
581{
582    TkMenu *menuPtr = NULL;
583    MenuID menuID = GetMenuID(menu);
584    Tcl_HashEntry *commandEntryPtr = Tcl_FindHashEntry(&commandTable,
585	    (char*)(intptr_t)menuID);
586
587    if (commandEntryPtr) {
588	menuPtr = (TkMenu *) Tcl_GetHashValue(commandEntryPtr);
589    }
590    return menuPtr;
591}
592
593/*
594 *----------------------------------------------------------------------
595 *
596 * GetParentMenuEntry --
597 *
598 *	Returns a pointer to the parent's TkMenuEntry of a given TkMenu.
599 *
600 * Results:
601 *	Returns a pointer to a TkMenuEntry or NULL.
602 *
603 * Side effects:
604 *	None.
605 *
606 *----------------------------------------------------------------------
607 */
608
609TkMenuEntry*
610GetParentMenuEntry(
611    TkMenu *menuPtr)
612{
613    TkMenuEntry *cascadeEntryPtr;
614
615    for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
616	    cascadeEntryPtr != NULL;
617	    cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
618	const char *name = (cascadeEntryPtr->namePtr == NULL) ? ""
619		: Tcl_GetString(cascadeEntryPtr->namePtr);
620
621	if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) {
622	    break;
623	}
624    }
625    return cascadeEntryPtr;
626}
627
628/*
629 *----------------------------------------------------------------------
630 *
631 * TkpNewMenu --
632 *
633 *	Gets a new blank menu. Only the platform specific options are filled
634 *	in.
635 *
636 * Results:
637 *	Returns a standard TCL error.
638 *
639 * Side effects:
640 *	Allocates a Macintosh menu handle and puts in the platformData
641 *	field of the menuPtr.
642 *
643 *----------------------------------------------------------------------
644 */
645
646int
647TkpNewMenu(
648    TkMenu *menuPtr)		/* The common structure we are making the
649				 * platform structure for. */
650{
651    short menuID;
652    MenuRef macMenuHdl;
653#ifdef USE_TK_MDEF
654    MenuDefSpec menuDefSpec;
655    Tcl_Obj *useMDEFObjPtr;
656    int useMDEF = 1;
657#endif
658    int error = TCL_OK;
659    OSStatus err;
660    CFStringRef cfStr;
661
662    error = TkMacOSXGetNewMenuID(menuPtr->interp, menuPtr, 0, &menuID);
663    if (error != TCL_OK) {
664	return error;
665    }
666    err = ChkErr(CreateNewMenu, menuID, kMenuAttrDoNotUseUserCommandKeys,
667	    &macMenuHdl);
668    if (err != noErr) {
669	Tcl_AppendResult(menuPtr->interp, "CreateNewMenu failed.", NULL);
670	return TCL_ERROR;
671    }
672    cfStr = CFStringCreateWithCString(NULL, Tk_PathName(menuPtr->tkwin),
673	    kCFStringEncodingUTF8);
674    if (!cfStr) {
675	Tcl_AppendResult(menuPtr->interp, "CFStringCreateWithCString failed.",
676		NULL);
677	return TCL_ERROR;
678    }
679    err = ChkErr(SetMenuTitleWithCFString, macMenuHdl, cfStr);
680    CFRelease(cfStr);
681    if (err != noErr) {
682	Tcl_AppendResult(menuPtr->interp, "SetMenuTitleWithCFString failed.",
683		NULL);
684	return TCL_ERROR;
685    }
686
687    menuPtr->platformData = (TkMenuPlatformData) ckalloc(sizeof(MacMenu));
688    ((MacMenu *) menuPtr->platformData)->menuHdl = macMenuHdl;
689
690#ifdef USE_TK_MDEF
691    /*
692     * Check whether we want to use the custom mdef or not. For now
693     * the default is to use it unless the variable is explicitly
694     * set to no.
695     */
696
697    useMDEFObjPtr = Tcl_ObjGetVar2(menuPtr->interp, useMDEFVar, NULL,
698	    TCL_GLOBAL_ONLY);
699    if (useMDEFObjPtr == NULL || Tcl_GetBooleanFromObj(NULL, useMDEFObjPtr,
700	    &useMDEF) == TCL_ERROR || useMDEF) {
701	menuDefSpec.defType = kMenuDefProcPtr;
702	menuDefSpec.u.defProc = MenuDefProc;
703	ChkErr(SetMenuDefinition, macMenuHdl, &menuDefSpec);
704    }
705    ((MacMenu *) menuPtr->platformData)->useMDEF = useMDEF;
706#endif /* USE_TK_MDEF */
707
708    if ((currentMenuBarInterp == menuPtr->interp)
709	    && (currentMenuBarName != NULL)) {
710	Tk_Window parentWin = Tk_Parent(menuPtr->tkwin);
711
712	if (strcmp(currentMenuBarName, Tk_PathName(parentWin)) == 0) {
713	    if ((strcmp(Tk_PathName(menuPtr->tkwin)
714		    + strlen(Tk_PathName(parentWin)), ".apple") == 0)
715		    || (strcmp(Tk_PathName(menuPtr->tkwin)
716		    + strlen(Tk_PathName(parentWin)), ".help") == 0)) {
717		if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) {
718		    Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL);
719		    menuBarFlags |= MENUBAR_REDRAW_PENDING;
720		}
721	    }
722	}
723    }
724
725    menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
726    Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr);
727    return TCL_OK;
728}
729
730/*
731 *----------------------------------------------------------------------
732 *
733 * TkpDestroyMenu --
734 *
735 *	Destroys platform-specific menu structures.
736 *
737 * Results:
738 *	None.
739 *
740 * Side effects:
741 *	All platform-specific allocations are freed up.
742 *
743 *----------------------------------------------------------------------
744 */
745
746void
747TkpDestroyMenu(
748    TkMenu *menuPtr)		/* The common menu structure */
749{
750    MenuRef macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl;
751
752    if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
753	Tcl_CancelIdleCall(ReconfigureMacintoshMenu, (ClientData) menuPtr);
754	menuPtr->menuFlags &= ~MENU_RECONFIGURE_PENDING;
755    }
756    if (GetMenuID(macMenuHdl) == currentHelpMenuID) {
757	MenuRef helpMenuHdl;
758	MenuItemIndex helpIndex;
759
760	if ((HMGetHelpMenu(&helpMenuHdl,&helpIndex) == noErr)
761		&& (helpMenuHdl != NULL)) {
762	    int i, count = CountMenuItems(helpMenuHdl);
763
764	    for (i = helpIndex; i <= count; i++) {
765		DeleteMenuItem(helpMenuHdl, helpIndex);
766	    }
767	}
768	currentHelpMenuID = 0;
769    }
770    if (menuPtr->platformData != NULL) {
771	MenuID menuID = GetMenuID(macMenuHdl);
772
773	DeleteMenu(menuID);
774	TkMacOSXFreeMenuID(menuID);
775	DisposeMenu(macMenuHdl);
776	ckfree((char *) menuPtr->platformData);
777	menuPtr->platformData = NULL;
778    }
779}
780
781/*
782 *----------------------------------------------------------------------
783 *
784 * SetMenuCascade --
785 *
786 *	Does any cleanup to change a menu from a normal to a cascade.
787 *
788 * Results:
789 *	Standard Tcl error.
790 *
791 * Side effects:
792 *	The mac menu id is reset.
793 *
794 *----------------------------------------------------------------------
795 */
796
797int
798SetMenuCascade(
799    TkMenu* menuPtr)		/* The menu we are setting up to be a
800				 * cascade. */
801{
802    MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl;
803    MenuID newMenuID, menuID = GetMenuID(macMenuHdl);
804    int error = TCL_OK;
805
806    if (menuID >= 256) {
807	error = TkMacOSXGetNewMenuID(menuPtr->interp, menuPtr, 1, &newMenuID);
808	if (error == TCL_OK) {
809	    TkMacOSXFreeMenuID(menuID);
810	    SetMenuID(macMenuHdl,newMenuID);
811	}
812    }
813    return error;
814}
815
816/*
817 *----------------------------------------------------------------------
818 *
819 * TkpDestroyMenuEntry --
820 *
821 *	Cleans up platform-specific menu entry items.
822 *
823 * Results:
824 *	None
825 *
826 * Side effects:
827 *	All platform-specific allocations are freed up.
828 *
829 *----------------------------------------------------------------------
830 */
831
832void
833TkpDestroyMenuEntry(
834    TkMenuEntry *mePtr)		/* The common structure for the menu entry. */
835{
836    TkMenu *menuPtr = mePtr->menuPtr;
837
838    ckfree((char *) mePtr->platformEntryData);
839    if ((menuPtr->platformData != NULL)
840	    && !(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
841	menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
842	Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr);
843    }
844}
845
846/*
847 *----------------------------------------------------------------------
848 *
849 * GetEntryText --
850 *
851 *	Given a menu entry, gives back the text that should go in it.
852 *	Separators should be done by the caller, as they have to be
853 *	handled specially. This is primarily used to do a substitution
854 *	between "..." and the ellipsis character which looks nicer.
855 *
856 * Results:
857 *	itemText points to the new text for the item.
858 *
859 * Side effects:
860 *	None.
861 *
862 *----------------------------------------------------------------------
863 */
864
865void
866GetEntryText(
867    TkMenuEntry *mePtr,		/* A pointer to the menu entry. */
868    Tcl_DString *dStringPtr)	/* The DString to put the text into. This
869				 * will be initialized by this routine. */
870{
871#ifdef USE_TK_MDEF
872    const int useMDEF = ((MacMenu *) mePtr->menuPtr->platformData)->useMDEF;
873#endif
874    int noLabel = (mePtr->labelPtr == NULL || mePtr->labelLength == 0);
875
876    Tcl_DStringInit(dStringPtr);
877    if (mePtr->type == TEAROFF_ENTRY && (useMDEF || noLabel)) {
878	Tcl_DStringAppend(dStringPtr, "(Tear-off)", -1);
879    } else if (mePtr->imagePtr != NULL && (useMDEF || noLabel) &&
880	    mePtr->compound == COMPOUND_NONE) {
881	Tcl_DStringAppend(dStringPtr, "(Image)", -1);
882    } else if (mePtr->bitmapPtr != NULL && (useMDEF || noLabel) &&
883	    mePtr->compound == COMPOUND_NONE) {
884	Tcl_DStringAppend(dStringPtr, "(Pixmap)", -1);
885    } else if (noLabel) {
886	/*
887	 * The Mac menu manager does not like null strings.
888	 */
889
890	Tcl_DStringAppend(dStringPtr, " ", -1);
891    } else {
892	int length;
893	char *text = Tcl_GetStringFromObj(mePtr->labelPtr, &length);
894	char *dStringText;
895	int i;
896
897	for (i = 0; *text; text++, i++) {
898	    if ((*text == '.') && (*(text+1) == '.') && (*(text+2) == '.')) {
899		Tcl_DStringAppend(dStringPtr, menuSymbols[ELLIPSIS_SYMBOL].utf,
900			menuSymbols[ELLIPSIS_SYMBOL].utfLen);
901		i += menuSymbols[ELLIPSIS_SYMBOL].utfLen - 1;
902		text += 2;
903	    } else {
904		Tcl_DStringSetLength(dStringPtr,
905			Tcl_DStringLength(dStringPtr) + 1);
906		dStringText = Tcl_DStringValue(dStringPtr);
907		dStringText[i] = *text;
908	    }
909	}
910    }
911}
912
913/*
914 *----------------------------------------------------------------------
915 *
916 * FindMarkCharacter --
917 *
918 *	Finds the Macintosh mark character based on the font of the
919 *	item. We calculate a good mark character based on the font
920 *	that this item is rendered in.
921 *
922 * Results:
923 *	Mark char.
924 *
925 * Side effects:
926 *	None.
927 *
928 *----------------------------------------------------------------------
929 */
930
931char
932FindMarkCharacter(
933    TkMenuEntry *mePtr)		/* The entry we are finding the character
934				 * for. */
935{
936    static const char markChars[] = {kCheckCharCode, kDiamondCharCode,
937	    kBulletCharCode, '-', kCheckCharCode};
938    const char *markChar = markChars;
939    int i = sizeof(markChars);
940    Tk_Font tkfont;
941
942    tkfont = Tk_GetFontFromObj(mePtr->menuPtr->tkwin,
943	    (mePtr->fontPtr == NULL) ? mePtr->menuPtr->fontPtr
944	    : mePtr->fontPtr);
945
946    while (--i) {
947	if (!TkMacOSXIsCharacterMissing(tkfont, *markChar)) {
948	    break;
949	}
950	markChar++;
951    }
952    return *markChar;
953}
954
955/*
956 *----------------------------------------------------------------------
957 *
958 * GetUtfMarkCharacter --
959 *
960 *	Get the utf8 string for the given mark character, taking into
961 *	account the special menu font char codes.
962 *
963 * Results:
964 *	Length of returned utf8 string.
965 *
966 * Side effects:
967 *	None.
968 *
969 *----------------------------------------------------------------------
970 */
971
972int
973GetUtfMarkCharacter(
974    char markChar,
975    const char **markUtfPtr)
976{
977    const MenuSymbol *ms = menuSymbols;
978    int len = 0;
979
980    while (ms->unicode) {
981	if (ms->charCode && ms->charCode == markChar) {
982	    *markUtfPtr = ms->utf;
983	    len = ms->utfLen;
984	    break;
985	}
986	ms++;
987    }
988    if (!len) {
989	static char markUtf[TCL_UTF_MAX + 1];
990
991	Tcl_ExternalToUtf(NULL, TkMacOSXCarbonEncoding, &markChar, 1, 0, NULL,
992		markUtf, TCL_UTF_MAX + 1, NULL, &len, NULL);
993	*markUtfPtr = markUtf;
994    }
995    return len;
996}
997
998/*
999 *----------------------------------------------------------------------
1000 *
1001 * ParseAccelerators --
1002 *
1003 *	Parse menu accelerator string.
1004 *
1005 * Results:
1006 *	Accelerator flags.
1007 *
1008 * Side effects:
1009 *	None.
1010 *
1011 *----------------------------------------------------------------------
1012 */
1013
1014int
1015ParseAccelerators(
1016    const char **accelStringPtr,
1017    int *modifierNumPtr,
1018    Tcl_UniChar *modifierUniChars,
1019    int *modifierWidth)
1020{
1021    struct Modif {
1022	const char *name;
1023	const size_t len;
1024	const int flag, symbol;
1025    };
1026#define MODIF(n, f) { #n, sizeof(#n)-1, ENTRY_##f##_ACCEL, f##_SYMBOL }
1027    static const struct Modif modifs[] = {
1028	MODIF(Control,	CONTROL),
1029	MODIF(Ctrl,	CONTROL),
1030	MODIF(Option,	OPTION),
1031	MODIF(Opt,	OPTION),
1032	MODIF(Alt,	OPTION),
1033	MODIF(Shift,	SHIFT),
1034	MODIF(Command,	COMMAND),
1035	MODIF(Cmd,	COMMAND),
1036	MODIF(Meta,	COMMAND),
1037	{ NULL, 0, 0, 0}
1038    };
1039#undef MODIF
1040    const char *accelString = *accelStringPtr;
1041    int flags = 0, num = 0, seen = 0, width = 0;
1042    const struct Modif *m;
1043
1044    while (1) {
1045	m = modifs;
1046	while (m->name) {
1047	    int l = m->len;
1048
1049	    if (!strncasecmp(accelString, m->name, l) &&
1050		    (accelString[l] == '-' || accelString[l] == '+')) {
1051		flags |= m->flag;
1052		accelString += l+1;
1053		break;
1054	    }
1055	    m++;
1056	}
1057	if (!m->name || !*accelString) {
1058	    break;
1059	}
1060    }
1061    m = modifs;
1062    while (m->name && num < MODIFIER_NUM) {
1063	if (flags & m->flag && !(seen & m->flag)) {
1064	    modifierUniChars[num++] = menuSymbols[m->symbol].unicode;
1065	    width += menuSymbols[m->symbol].width;
1066	    seen |= m->flag;
1067	}
1068	m++;
1069    }
1070    *accelStringPtr = accelString;
1071    *modifierNumPtr = num;
1072    *modifierWidth = width;
1073    return flags;
1074}
1075
1076/*
1077 *----------------------------------------------------------------------
1078 *
1079 * TkpConfigureMenuEntry --
1080 *
1081 *	Processes configurations for menu entries.
1082 *
1083 * Results:
1084 *	Returns standard TCL result. If TCL_ERROR is returned, then
1085 *	the interp's result contains an error message.
1086 *
1087 * Side effects:
1088 *	Configuration information get set for mePtr; old resources
1089 *	get freed, if any need it.
1090 *
1091 *----------------------------------------------------------------------
1092 */
1093
1094int
1095TkpConfigureMenuEntry(
1096    TkMenuEntry *mePtr) 	/* Information about menu entry; may
1097				 * or may not already have values for
1098				 * some fields. */
1099{
1100    TkMenu *menuPtr = mePtr->menuPtr;
1101    EntryGeometry *geometryPtr = (EntryGeometry *) mePtr->platformEntryData;
1102
1103    /*
1104     * Cascade menus have to have menu IDs of less than 256. So
1105     * we need to change the child menu if this has been configured
1106     * for a cascade item.
1107     */
1108
1109    if (mePtr->type == CASCADE_ENTRY) {
1110	if ((mePtr->childMenuRefPtr != NULL)
1111		&& (mePtr->childMenuRefPtr->menuPtr != NULL)) {
1112	    MenuHandle childMenuHdl = ((MacMenu *) mePtr
1113		    ->childMenuRefPtr->menuPtr->platformData)->menuHdl;
1114
1115	    if (childMenuHdl != NULL) {
1116		int error = SetMenuCascade(mePtr->childMenuRefPtr->menuPtr);
1117
1118		if (error != TCL_OK) {
1119		    return error;
1120		}
1121
1122		if (menuPtr->menuType == MENUBAR) {
1123		    CFStringRef cfStr = CFStringCreateWithCString(NULL,
1124			    (!(mePtr->labelPtr) ? "" :
1125			    Tcl_GetString(mePtr->labelPtr)),
1126			    kCFStringEncodingUTF8);
1127
1128		    if (cfStr) {
1129			SetMenuTitleWithCFString(childMenuHdl, cfStr);
1130			CFRelease(cfStr);
1131		    }
1132		}
1133	    }
1134	}
1135    }
1136
1137    /*
1138     * We need to parse the accelerator string. If it has the strings
1139     * for Command, Control, Shift or Option, we need to flag it
1140     * so we can draw the symbols for it. We also need to precalcuate
1141     * the position of the first real character we are drawing.
1142     */
1143
1144    if (0 == mePtr->accelLength) {
1145	geometryPtr->accelTextStart = -1;
1146    } else {
1147	const char *accelString = (mePtr->accelPtr == NULL) ? ""
1148		: Tcl_GetString(mePtr->accelPtr);
1149	const char *accelStart = accelString;
1150
1151	mePtr->entryFlags &= ~ENTRY_ACCEL_MASK;
1152	mePtr->entryFlags |= ParseAccelerators(&accelString,
1153		&geometryPtr->modifierNum, geometryPtr->modifierUniChars,
1154		&geometryPtr->modifierWidth);
1155	geometryPtr->accelTextStart = (ptrdiff_t)(accelString - accelStart);
1156    }
1157
1158    if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
1159	menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
1160	Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr);
1161    }
1162
1163    return TCL_OK;
1164}
1165
1166/*
1167 *----------------------------------------------------------------------
1168 *
1169 * ReconfigureIndividualMenu --
1170 *
1171 *	This routine redoes the guts of the menu. It works from
1172 *	a base item and offset, so that a regular menu will
1173 *	just have all of its items added, but the help menu will
1174 *	have all of its items appended after the apple-defined
1175 *	items.
1176 *
1177 * Results:
1178 *	None.
1179 *
1180 * Side effects:
1181 *	The Macintosh menu handle is updated
1182 *
1183 *----------------------------------------------------------------------
1184 */
1185
1186void
1187ReconfigureIndividualMenu(
1188    TkMenu *menuPtr,		/* The menu we are affecting. */
1189    MenuHandle macMenuHdl,	/* The macintosh menu we are affecting.
1190				 * Will not necessarily be
1191				 * menuPtr->platformData because this could
1192				 * be the help menu. */
1193    int base)			/* The last index that we do not want
1194				 * touched. 0 for normal menus;
1195				 * # of system help menu items
1196				 * for help menus. */
1197{
1198    int count;
1199    int index;
1200    TkMenuEntry *mePtr;
1201    int parentDisabled = 0;
1202
1203#ifdef TK_MAC_DEBUG_MENUS
1204    /*
1205     * Carbon-internal menu debugging (c.f. Technote 2124)
1206     */
1207
1208    TkMacOSXInitNamedDebugSymbol(HIToolbox, void, DebugPrintMenu,
1209	MenuRef menu);
1210    if (DebugPrintMenu) {
1211	DebugPrintMenu(macMenuHdl);
1212    }
1213#endif
1214
1215    mePtr = GetParentMenuEntry(menuPtr);
1216    if (mePtr && mePtr->state == ENTRY_DISABLED) {
1217	parentDisabled = 1;
1218    }
1219
1220    /*
1221     * First, we get rid of all of the old items.
1222     */
1223
1224    count = CountMenuItems(macMenuHdl);
1225    for (index = base; index < count; index++) {
1226	DeleteMenuItem(macMenuHdl, base + 1);
1227    }
1228
1229    count = menuPtr->numEntries;
1230
1231    for (index = 1; index <= count; index++) {
1232	mePtr = menuPtr->entries[index - 1];
1233
1234	/*
1235	 * We have to do separators separately because SetMenuItemText
1236	 * does not parse meta-characters.
1237	 */
1238
1239	if (mePtr->type == SEPARATOR_ENTRY) {
1240	    AppendMenuItemTextWithCFString(macMenuHdl, NULL,
1241		    kMenuItemAttrSeparator | kMenuItemAttrDisabled, 0, NULL);
1242	} else {
1243	    Tcl_DString itemTextDString;
1244	    CFStringRef cfStr;
1245
1246	    GetEntryText(mePtr, &itemTextDString);
1247	    cfStr = CFStringCreateWithCString(NULL,
1248		    Tcl_DStringValue(&itemTextDString), kCFStringEncodingUTF8);
1249	    if (cfStr) {
1250		AppendMenuItemTextWithCFString(macMenuHdl, cfStr, 0, 0, NULL);
1251		CFRelease(cfStr);
1252	    } else {
1253		AppendMenuItemTextWithCFString(macMenuHdl, CFSTR ("<Error>"),
1254		    0, 0, NULL);
1255	    }
1256	    Tcl_DStringFree(&itemTextDString);
1257
1258	    /*
1259	     * Set enabling and disabling correctly.
1260	     */
1261
1262	    if (parentDisabled || (mePtr->state == ENTRY_DISABLED)) {
1263		DisableMenuItem(macMenuHdl, base + index);
1264	    } else {
1265		EnableMenuItem(macMenuHdl, base + index);
1266	    }
1267
1268	    /*
1269	     * Set the check mark for check entries and radio entries.
1270	     */
1271
1272	    SetItemMark(macMenuHdl, base + index, 0);
1273	    if ((mePtr->type == CHECK_BUTTON_ENTRY)
1274		    || (mePtr->type == RADIO_BUTTON_ENTRY)) {
1275		CheckMenuItem(macMenuHdl, base + index, (mePtr->entryFlags
1276		& ENTRY_SELECTED) && mePtr->indicatorOn);
1277		if (mePtr->indicatorOn
1278			&& (mePtr->entryFlags & ENTRY_SELECTED)) {
1279		    SetItemMark(macMenuHdl, base + index,
1280			    FindMarkCharacter(mePtr));
1281		}
1282	    }
1283
1284	    if (mePtr->type == CASCADE_ENTRY) {
1285		if ((mePtr->childMenuRefPtr != NULL)
1286			&& (mePtr->childMenuRefPtr->menuPtr != NULL)) {
1287		    MenuHandle childMenuHdl =
1288			    ((MacMenu *) mePtr->childMenuRefPtr
1289			    ->menuPtr->platformData)->menuHdl;
1290
1291		    if (childMenuHdl != NULL) {
1292			ChkErr(SetMenuItemHierarchicalID, macMenuHdl,
1293				base + index, GetMenuID(childMenuHdl));
1294		    }
1295		    /*
1296		     * If we changed the highligthing of this menu, its
1297		     * children all have to be reconfigured so that
1298		     * their state will be reflected in the menubar.
1299		     */
1300
1301		    if (!(mePtr->childMenuRefPtr->menuPtr->menuFlags
1302				& MENU_RECONFIGURE_PENDING)) {
1303			mePtr->childMenuRefPtr->menuPtr->menuFlags
1304				|= MENU_RECONFIGURE_PENDING;
1305			Tcl_DoWhenIdle(ReconfigureMacintoshMenu,
1306				(ClientData) mePtr->childMenuRefPtr->menuPtr);
1307		    }
1308		}
1309	    }
1310
1311	    if ((mePtr->type != CASCADE_ENTRY) && (mePtr->accelPtr != NULL)) {
1312		int accelLen, modifiers = 0, hasCmd = 0;
1313		EntryGeometry *geometryPtr =
1314			(EntryGeometry*)mePtr->platformEntryData;
1315		int offset = geometryPtr->accelTextStart;
1316		char *accel = Tcl_GetStringFromObj(mePtr->accelPtr, &accelLen);
1317
1318		accelLen -= offset;
1319		accel += offset;
1320		if (mePtr->entryFlags & ENTRY_OPTION_ACCEL) {
1321		    modifiers |= kMenuOptionModifier;
1322		}
1323		if (mePtr->entryFlags & ENTRY_SHIFT_ACCEL) {
1324		    modifiers |= kMenuShiftModifier;
1325		}
1326		if (mePtr->entryFlags & ENTRY_CONTROL_ACCEL) {
1327		    modifiers |= kMenuControlModifier;
1328		}
1329		if (mePtr->entryFlags & ENTRY_COMMAND_ACCEL) {
1330		    hasCmd = 1;
1331		}
1332		if (accelLen == 1) {
1333		    if (hasCmd || (modifiers != 0 && modifiers !=
1334			    kMenuShiftModifier)) {
1335			SetItemCmd(macMenuHdl, base + index, accel[0]);
1336			if (!hasCmd) {
1337			    modifiers |= kMenuNoCommandModifier;
1338			}
1339		    }
1340		} else {
1341		    /*
1342		     * Convert from accelerator names to Carbon menu glyphs.
1343		     */
1344		    struct Glyph {
1345			const char *name;
1346			const size_t len;
1347			const char glyph;
1348		    };
1349#define GLYPH(n, g) { #n, sizeof(#n)-1, kMenu##g##Glyph }
1350		    static const struct Glyph glyphs[] = {
1351			GLYPH(PageUp,	PageUp),
1352			GLYPH(PageDown, PageDown),
1353			GLYPH(Left,	LeftArrow),
1354			GLYPH(Right,	RightArrow),
1355			GLYPH(Up,	UpArrow),
1356			GLYPH(Down,	DownArrow),
1357			GLYPH(Escape,	Escape),
1358			GLYPH(Clear,	Clear),
1359			GLYPH(Enter,	Enter),
1360			GLYPH(Backspace,DeleteLeft),
1361			GLYPH(Space,	Space),
1362			GLYPH(Tab,	TabRight),
1363			GLYPH(Delete,	DeleteRight),
1364			GLYPH(Home,	NorthwestArrow),
1365			GLYPH(End,	SoutheastArrow),
1366			GLYPH(Return,	Return),
1367			GLYPH(Help,	Help),
1368			GLYPH(Power,	Power),
1369			{ NULL, 0, 0}
1370		    };
1371#undef GLYPH
1372		    const struct Glyph *g = glyphs;
1373		    char glyph = 0;
1374
1375		    if (accel[0] == 'F' && accelLen < 4 &&
1376			    (accel[1] > '0' && accel[1] <= '9')) {
1377			int fkey = accel[1] - '0';
1378
1379			if (accelLen == 3) {
1380			    if (accel[2] >= '0' && accel[2] <= '9') {
1381				fkey = 10 * fkey + (accel[2] - '0');
1382			    } else {
1383				fkey = 0;
1384			    }
1385			}
1386			if (fkey >= 1 && fkey <= 12) {
1387			    glyph = kMenuF1Glyph + fkey - 1;
1388			} else if (fkey >= 13 && fkey <= 15) {
1389			    glyph = kMenuF13Glyph + fkey - 13;
1390			}
1391		    } else while (g->name) {
1392			if (accel[0] == g->name[0] &&
1393				(size_t)accelLen == g->len &&
1394				!strncasecmp(accel, g->name, g->len)) {
1395			    glyph = g->glyph;
1396			    break;
1397			}
1398			g++;
1399		    }
1400		    if (glyph) {
1401			ChkErr(SetMenuItemKeyGlyph, macMenuHdl, base + index,
1402				glyph);
1403			if (!hasCmd) {
1404			    modifiers |= kMenuNoCommandModifier;
1405			}
1406			geometryPtr->accelGlyph = glyph;
1407		    }
1408		}
1409		ChkErr(SetMenuItemModifiers, macMenuHdl, base + index,
1410			modifiers);
1411	    }
1412	}
1413    }
1414}
1415
1416/*
1417 *----------------------------------------------------------------------
1418 *
1419 * ReconfigureMacintoshMenu --
1420 *
1421 *	Rebuilds the Macintosh MenuHandle items from the menu. Called
1422 *	usually as an idle handler, but can be called synchronously
1423 *	if the menu is about to be posted.
1424 *
1425 * Results:
1426 *	None.
1427 *
1428 * Side effects:
1429 *	Configuration information get set for mePtr; old resources
1430 *	get freed, if any need it.
1431 *
1432 *----------------------------------------------------------------------
1433 */
1434
1435void
1436ReconfigureMacintoshMenu(
1437    ClientData clientData)	/* Information about menu entry; may
1438				 * or may not already have values for
1439				 * some fields. */
1440{
1441    TkMenu *menuPtr = (TkMenu *) clientData;
1442    MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl;
1443    MenuHandle helpMenuHdl = NULL;
1444
1445    menuPtr->menuFlags &= ~MENU_RECONFIGURE_PENDING;
1446
1447    if (NULL == macMenuHdl) {
1448	return;
1449    }
1450
1451    ReconfigureIndividualMenu(menuPtr, macMenuHdl, 0);
1452
1453    if (GetMenuID(macMenuHdl) == currentHelpMenuID) {
1454	MenuItemIndex helpIndex;
1455	HMGetHelpMenu(&helpMenuHdl,&helpIndex);
1456	if (helpMenuHdl != NULL) {
1457	    ReconfigureIndividualMenu(menuPtr, helpMenuHdl, helpIndex - 1);
1458	}
1459    }
1460
1461    if (menuPtr->menuType == MENUBAR) {
1462	if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) {
1463	    Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL);
1464	    menuBarFlags |= MENUBAR_REDRAW_PENDING;
1465	}
1466    }
1467}
1468
1469/*
1470 *----------------------------------------------------------------------
1471 *
1472 * CompleteIdlers --
1473 *
1474 *	Completes all idle handling so that the menus are in sync when
1475 *	the user invokes them with the mouse.
1476 *
1477 * Results:
1478 *	None.
1479 *
1480 * Side effects:
1481 *	The Macintosh menu handles are flushed out.
1482 *
1483 *----------------------------------------------------------------------
1484 */
1485
1486void
1487CompleteIdlers(
1488    TkMenu *menuPtr)		/* The menu we are completing. */
1489{
1490    int i;
1491
1492    if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
1493	Tcl_CancelIdleCall(ReconfigureMacintoshMenu, (ClientData) menuPtr);
1494	ReconfigureMacintoshMenu((ClientData) menuPtr);
1495    }
1496
1497    for (i = 0; i < menuPtr->numEntries; i++) {
1498	if ((menuPtr->entries[i]->type == CASCADE_ENTRY) &&
1499		(menuPtr->entries[i]->childMenuRefPtr != NULL) &&
1500		(menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) {
1501	    CompleteIdlers(menuPtr->entries[i]->childMenuRefPtr->menuPtr);
1502	}
1503    }
1504}
1505
1506/*
1507 *----------------------------------------------------------------------
1508 *
1509 * TkpPostMenu --
1510 *
1511 *	Posts a menu on the screen
1512 *
1513 * Results:
1514 *	None.
1515 *
1516 * Side effects:
1517 *	The menu is posted and handled.
1518 *
1519 *----------------------------------------------------------------------
1520 */
1521
1522int
1523TkpPostMenu(
1524    Tcl_Interp *interp,		/* The interpreter this menu lives in */
1525    TkMenu *menuPtr,		/* The menu we are posting */
1526    int x,			/* The global x-coordinate of the top, left-
1527				 * hand corner of where the menu is supposed
1528				 * to be posted. */
1529    int y)			/* The global y-coordinate */
1530{
1531    MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl;
1532    long popUpResult;
1533    int result;
1534
1535    if (inPostMenu > 0) {
1536	Tcl_AppendResult(interp,
1537		"Cannot call post menu while already posting menu", NULL);
1538	result = TCL_ERROR;
1539    } else {
1540	short menuID;
1541	Window window;
1542	int oldWidth = menuPtr->totalWidth;
1543
1544	inPostMenu++;
1545	result = TkPreprocessMenu(menuPtr);
1546	/*
1547	 * The post commands could have deleted the menu, which means
1548	 * we are dead and should go away.
1549	 */
1550
1551	if (result != TCL_OK || !menuPtr->tkwin) {
1552	    goto endPostMenu;
1553	}
1554
1555	CompleteIdlers(menuPtr);
1556	if (menuBarFlags & MENUBAR_REDRAW_PENDING) {
1557	    Tcl_CancelIdleCall(DrawMenuBarWhenIdle, NULL);
1558	    DrawMenuBarWhenIdle(NULL);
1559	}
1560	RecursivelyInsertMenu(menuPtr);
1561
1562	TkMacOSXTrackingLoop(1);
1563	popUpResult = PopUpMenuSelect(macMenuHdl, y, x, menuPtr->active);
1564	TkMacOSXTrackingLoop(0);
1565	menuPtr->totalWidth = oldWidth;
1566
1567	/*
1568	 * Simulate the mouse up.
1569	 */
1570
1571	window = Tk_WindowId(menuPtr->tkwin);
1572	TkGenerateButtonEventForXPointer(window);
1573
1574	/*
1575	 * Dispatch the command.
1576	 */
1577
1578	menuID = HiWord(popUpResult);
1579	if (menuID != 0) {
1580	    result = TkMacOSXDispatchMenuEvent(menuID, LoWord(popUpResult));
1581	}
1582
1583endPostMenu:
1584	inPostMenu--;
1585    }
1586    return result;
1587}
1588
1589/*
1590 *----------------------------------------------------------------------
1591 *
1592 * TkpMenuNewEntry --
1593 *
1594 *	Adds a pointer to a new menu entry structure with the platform-
1595 *	specific fields filled in. The Macintosh uses the
1596 *	platformEntryData field of the TkMenuEntry record to store
1597 *	geometry information.
1598 *
1599 * Results:
1600 *	Standard TCL error.
1601 *
1602 * Side effects:
1603 *	Storage gets allocated. New menu entry data is put into the
1604 *	platformEntryData field of the mePtr.
1605 *
1606 *----------------------------------------------------------------------
1607 */
1608
1609int
1610TkpMenuNewEntry(
1611    TkMenuEntry *mePtr)		/* The menu we are adding an entry to */
1612{
1613    EntryGeometry *geometryPtr =
1614	    (EntryGeometry *) ckalloc(sizeof(EntryGeometry));
1615    TkMenu *menuPtr = mePtr->menuPtr;
1616
1617    geometryPtr->accelTextStart = 0;
1618    geometryPtr->accelTextWidth = 0;
1619    geometryPtr->nonAccelMargin = 0;
1620    geometryPtr->modifierWidth = 0;
1621    geometryPtr->modifierNum = 0;
1622    geometryPtr->accelGlyph = 0;
1623    mePtr->platformEntryData = (TkMenuPlatformEntryData) geometryPtr;
1624    if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
1625	menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
1626	Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr);
1627    }
1628    return TCL_OK;
1629}
1630
1631/*
1632 *----------------------------------------------------------------------
1633 *
1634 * Tk_MacOSXTurnOffMenus --
1635 *
1636 *	Turns off all the menu drawing code. This is more than just disabling
1637 *	the "menu" command, this means that Tk will NEVER touch the menubar.
1638 *	It is needed in the Plugin, where Tk does not own the menubar.
1639 *
1640 * Results:
1641 *	None.
1642 *
1643 * Side effects:
1644 *	A flag is set which will disable all menu drawing.
1645 *
1646 *----------------------------------------------------------------------
1647 */
1648
1649void
1650Tk_MacOSXTurnOffMenus(void)
1651{
1652    gNoTkMenus = 1;
1653}
1654
1655/*
1656 *----------------------------------------------------------------------
1657 *
1658 * DrawMenuBarWhenIdle --
1659 *
1660 *	Update the menu bar next time there is an idle event.
1661 *
1662 * Results:
1663 *	None.
1664 *
1665 * Side effects:
1666 *	Menu bar is redrawn.
1667 *
1668 *----------------------------------------------------------------------
1669 */
1670
1671void
1672DrawMenuBarWhenIdle(
1673    ClientData clientData)	/* ignored here */
1674{
1675    TkMenuReferences *menuRefPtr;
1676    TkMenu *appleMenuPtr, *helpMenuPtr, *menuBarPtr = NULL;
1677    MenuHandle macMenuHdl;
1678    Tcl_HashEntry *hashEntryPtr;
1679
1680    /*
1681     * If we have been turned off, exit.
1682     */
1683
1684    if (gNoTkMenus) {
1685	return;
1686    }
1687
1688    /*
1689     * We need to clear the apple and help menus of any extra items.
1690     */
1691
1692    if (currentAppleMenuID != 0) {
1693	hashEntryPtr = Tcl_FindHashEntry(&commandTable,
1694		(char*)(intptr_t)currentAppleMenuID);
1695	appleMenuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
1696	TkpDestroyMenu(appleMenuPtr);
1697	TkpNewMenu(appleMenuPtr);
1698	appleMenuPtr->menuFlags &= ~MENU_APPLE_MENU;
1699	appleMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
1700	Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) appleMenuPtr);
1701    }
1702
1703    if (currentHelpMenuID != 0) {
1704	hashEntryPtr = Tcl_FindHashEntry(&commandTable,
1705		(char*)(intptr_t)currentHelpMenuID);
1706	helpMenuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
1707	TkpDestroyMenu(helpMenuPtr);
1708	TkpNewMenu(helpMenuPtr);
1709	helpMenuPtr->menuFlags &= ~MENU_HELP_MENU;
1710	helpMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
1711	Tcl_DoWhenIdle(ReconfigureMacintoshMenu,
1712		(ClientData) helpMenuPtr);
1713    }
1714
1715    /*
1716     * We need to find the clone of this menu that is the menubar.
1717     * Once we do that, for every cascade in the menu, we need to
1718     * insert the Mac menu in the Mac menubar. Finally, we need
1719     * to redraw the menubar.
1720     */
1721
1722    menuRefPtr = NULL;
1723    if (currentMenuBarName != NULL) {
1724	menuRefPtr = TkFindMenuReferences(currentMenuBarInterp,
1725		currentMenuBarName);
1726    }
1727    if (menuRefPtr) {
1728	TkMenu *menuPtr;
1729	TkMenu *cascadeMenuPtr;
1730	char *appleMenuName, *helpMenuName;
1731	int appleIndex = -1, helpIndex = -1, i;
1732
1733	menuPtr = menuRefPtr->menuPtr;
1734	if (menuPtr != NULL) {
1735	    TkMenuReferences *specialMenuRefPtr;
1736	    TkMenuEntry *specialEntryPtr;
1737
1738	    appleMenuName = ckalloc(strlen(currentMenuBarName) + 1 +
1739		    strlen(".apple") + 1);
1740	    sprintf(appleMenuName, "%s.apple", Tk_PathName(menuPtr->tkwin));
1741	    specialMenuRefPtr = TkFindMenuReferences(currentMenuBarInterp,
1742		    appleMenuName);
1743	    if ((specialMenuRefPtr != NULL)
1744		    && (specialMenuRefPtr->menuPtr != NULL)) {
1745		for (specialEntryPtr = specialMenuRefPtr->parentEntryPtr;
1746			specialEntryPtr != NULL;
1747			specialEntryPtr = specialEntryPtr->nextCascadePtr) {
1748		    if (specialEntryPtr->menuPtr == menuPtr) {
1749			appleIndex = specialEntryPtr->index;
1750			break;
1751		    }
1752		}
1753	    }
1754	    ckfree(appleMenuName);
1755
1756	    helpMenuName = ckalloc(strlen(currentMenuBarName) + 1 +
1757		    strlen(".help") + 1);
1758	    sprintf(helpMenuName, "%s.help", Tk_PathName(menuPtr->tkwin));
1759	    specialMenuRefPtr = TkFindMenuReferences(currentMenuBarInterp,
1760		    helpMenuName);
1761	    if ((specialMenuRefPtr != NULL)
1762		    && (specialMenuRefPtr->menuPtr != NULL)) {
1763		for (specialEntryPtr = specialMenuRefPtr->parentEntryPtr;
1764			specialEntryPtr != NULL;
1765			specialEntryPtr = specialEntryPtr->nextCascadePtr) {
1766		    if (specialEntryPtr->menuPtr == menuPtr) {
1767			helpIndex = specialEntryPtr->index;
1768			break;
1769		    }
1770		}
1771	    }
1772	    ckfree(helpMenuName);
1773	}
1774
1775	for (menuBarPtr = menuPtr;
1776		(menuBarPtr != NULL) && (menuBarPtr->menuType != MENUBAR);
1777		menuBarPtr = menuBarPtr->nextInstancePtr) {
1778	    /*
1779	     * Null loop body.
1780	     */
1781	}
1782
1783	if (menuBarPtr) {
1784	    if (menuBarPtr->tearoff != menuPtr->tearoff) {
1785		if (menuBarPtr->tearoff) {
1786		    appleIndex = (-1 == appleIndex) ? appleIndex
1787			    : appleIndex + 1;
1788		    helpIndex = (-1 == helpIndex) ? helpIndex
1789			    : helpIndex + 1;
1790		} else {
1791		    appleIndex = (-1 == appleIndex) ? appleIndex
1792			    : appleIndex - 1;
1793		    helpIndex = (-1 == helpIndex) ? helpIndex
1794			    : helpIndex - 1;
1795		}
1796	    }
1797	    ClearMenuBar();
1798
1799	    if (appleIndex == -1) {
1800		InsertMenu(tkAppleMenu, 0);
1801		currentAppleMenuID = 0;
1802		tkCurrentAppleMenu = tkAppleMenu;
1803	    } else {
1804		short appleID;
1805
1806		appleMenuPtr = menuBarPtr->entries[appleIndex]
1807			->childMenuRefPtr->menuPtr;
1808		TkpDestroyMenu(appleMenuPtr);
1809		TkMacOSXGetNewMenuID(appleMenuPtr->interp, appleMenuPtr, 0,
1810			&appleID);
1811		macMenuHdl = NewMenu(appleID, "\p\024");
1812		appleMenuPtr->platformData =
1813			(TkMenuPlatformData) ckalloc(sizeof(MacMenu));
1814		((MacMenu *)appleMenuPtr->platformData)->menuHdl
1815			= macMenuHdl;
1816		appleMenuPtr->menuFlags |= MENU_APPLE_MENU;
1817		if (!(appleMenuPtr->menuFlags
1818			& MENU_RECONFIGURE_PENDING)) {
1819		    appleMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
1820		    Tcl_DoWhenIdle(ReconfigureMacintoshMenu,
1821			    (ClientData) appleMenuPtr);
1822		}
1823		InsertMenu(macMenuHdl, 0);
1824		RecursivelyInsertMenu(appleMenuPtr);
1825		currentAppleMenuID = appleID;
1826		tkCurrentAppleMenu = macMenuHdl;
1827	    }
1828	    if (helpIndex == -1) {
1829		currentHelpMenuID = 0;
1830	    }
1831
1832	    for (i = 0; i < menuBarPtr->numEntries; i++) {
1833		if (i == appleIndex) {
1834		    if (menuBarPtr->entries[i]->state == ENTRY_DISABLED) {
1835			DisableMenuItem(((MacMenu *) menuBarPtr->entries[i]
1836				->childMenuRefPtr->menuPtr
1837				->platformData)->menuHdl, 0);
1838		    } else {
1839			EnableMenuItem(((MacMenu *) menuBarPtr->entries[i]
1840				->childMenuRefPtr->menuPtr
1841				->platformData)->menuHdl, 0);
1842		    }
1843		    continue;
1844		} else if (i == helpIndex) {
1845		    TkMenu *helpMenuPtr = menuBarPtr->entries[i]
1846			    ->childMenuRefPtr->menuPtr;
1847
1848		    if (helpMenuPtr == NULL) {
1849			continue;
1850		    }
1851		    helpMenuPtr->menuFlags |= MENU_HELP_MENU;
1852		    if (!(helpMenuPtr->menuFlags
1853			    & MENU_RECONFIGURE_PENDING)) {
1854			helpMenuPtr->menuFlags
1855				|= MENU_RECONFIGURE_PENDING;
1856			Tcl_DoWhenIdle(ReconfigureMacintoshMenu,
1857				(ClientData) helpMenuPtr);
1858		    }
1859		    macMenuHdl =
1860			    ((MacMenu *) helpMenuPtr->platformData)->menuHdl;
1861		    currentHelpMenuID = GetMenuID(macMenuHdl);
1862		} else if (menuBarPtr->entries[i]->type
1863			== CASCADE_ENTRY) {
1864		    if ((menuBarPtr->entries[i]->childMenuRefPtr != NULL)
1865			    && menuBarPtr->entries[i]->childMenuRefPtr
1866			    ->menuPtr != NULL) {
1867			cascadeMenuPtr = menuBarPtr->entries[i]
1868				->childMenuRefPtr->menuPtr;
1869			macMenuHdl = ((MacMenu *) cascadeMenuPtr
1870				->platformData)->menuHdl;
1871			DeleteMenu(GetMenuID(macMenuHdl));
1872			InsertMenu(macMenuHdl, 0);
1873			RecursivelyInsertMenu(cascadeMenuPtr);
1874			if (menuBarPtr->entries[i]->state == ENTRY_DISABLED) {
1875			    DisableMenuItem(((MacMenu *) menuBarPtr->entries[i]
1876				    ->childMenuRefPtr->menuPtr
1877				    ->platformData)->menuHdl, 0);
1878			} else {
1879			    EnableMenuItem(((MacMenu *) menuBarPtr->entries[i]
1880				    ->childMenuRefPtr->menuPtr
1881				    ->platformData)->menuHdl, 0);
1882			 }
1883		    }
1884		}
1885	    }
1886	}
1887    }
1888    if (!menuRefPtr || !menuBarPtr) {
1889	SetDefaultMenubar();
1890    }
1891    DrawMenuBar();
1892    menuBarFlags &= ~MENUBAR_REDRAW_PENDING;
1893}
1894
1895/*
1896 *----------------------------------------------------------------------
1897 *
1898 * RecursivelyInsertMenu --
1899 *
1900 *	Puts all of the cascades of this menu in the Mac hierarchical list.
1901 *
1902 * Results:
1903 *	None.
1904 *
1905 * Side effects:
1906 *	The menubar is changed.
1907 *
1908 *----------------------------------------------------------------------
1909 */
1910
1911void
1912RecursivelyInsertMenu(
1913    TkMenu *menuPtr)		/* All of the cascade items in this menu
1914				 * will be inserted into the mac menubar. */
1915{
1916    int i;
1917    TkMenu *cascadeMenuPtr;
1918    MenuHandle macMenuHdl;
1919
1920    for (i = 0; i < menuPtr->numEntries; i++) {
1921	if (menuPtr->entries[i]->type == CASCADE_ENTRY) {
1922	    if ((menuPtr->entries[i]->childMenuRefPtr != NULL) &&
1923		    (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) {
1924		cascadeMenuPtr = menuPtr->entries[i]->childMenuRefPtr->menuPtr;
1925		macMenuHdl =
1926			((MacMenu *) cascadeMenuPtr->platformData)->menuHdl;
1927		InsertMenu(macMenuHdl, -1);
1928		RecursivelyInsertMenu(cascadeMenuPtr);
1929	    }
1930	}
1931    }
1932}
1933
1934/*
1935 *----------------------------------------------------------------------
1936 *
1937 * RecursivelyDeleteMenu --
1938 *
1939 *	Takes all of the cascades of this menu out of the Mac hierarchical
1940 *	list.
1941 *
1942 * Results:
1943 *	None.
1944 *
1945 * Side effects:
1946 *	The menubar is changed.
1947 *
1948 *----------------------------------------------------------------------
1949 */
1950
1951void
1952RecursivelyDeleteMenu(
1953    TkMenu *menuPtr)		/* All of the cascade items in this menu
1954				 * will be deleted from the mac menubar. */
1955{
1956    int i;
1957    TkMenu *cascadeMenuPtr;
1958    MenuHandle macMenuHdl;
1959
1960    for (i = 0; i < menuPtr->numEntries; i++) {
1961	if (menuPtr->entries[i]->type == CASCADE_ENTRY) {
1962	    if ((menuPtr->entries[i]->childMenuRefPtr != NULL) &&
1963		    (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) {
1964		cascadeMenuPtr = menuPtr->entries[i]->childMenuRefPtr->menuPtr;
1965		macMenuHdl =
1966			((MacMenu *) cascadeMenuPtr->platformData)->menuHdl;
1967		DeleteMenu(GetMenuID(macMenuHdl));
1968		RecursivelyDeleteMenu(cascadeMenuPtr);
1969	    }
1970	}
1971    }
1972}
1973
1974/*
1975 *----------------------------------------------------------------------
1976 *
1977 * SetDefaultMenubar --
1978 *
1979 *	Puts the Apple, File and Edit menus into the Macintosh menubar.
1980 *
1981 * Results:
1982 *	None.
1983 *
1984 * Side effects:
1985 *	The menubar is changed.
1986 *
1987 *----------------------------------------------------------------------
1988 */
1989
1990void
1991SetDefaultMenubar(void)
1992{
1993    if (currentMenuBarName != NULL) {
1994	ckfree(currentMenuBarName);
1995	currentMenuBarName = NULL;
1996    }
1997    currentMenuBarOwner = NULL;
1998    ClearMenuBar();
1999    InsertMenu(tkAppleMenu, 0);
2000    InsertMenu(tkFileMenu, 0);
2001    InsertMenu(tkEditMenu, 0);
2002    if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) {
2003	Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL);
2004	menuBarFlags |= MENUBAR_REDRAW_PENDING;
2005    }
2006}
2007
2008/*
2009 *----------------------------------------------------------------------
2010 *
2011 * TkpSetMainMenubar --
2012 *
2013 *	Puts the menu associated with a window into the menubar. Should
2014 *	only be called when the window is in front.
2015 *
2016 * Results:
2017 *	None.
2018 *
2019 * Side effects:
2020 *	The menubar is changed.
2021 *
2022 *----------------------------------------------------------------------
2023 */
2024
2025void
2026TkpSetMainMenubar(
2027    Tcl_Interp *interp,		/* The interpreter of the application */
2028    Tk_Window tkwin,		/* The frame we are setting up */
2029    char *menuName)		/* The name of the menu to put in front.
2030				 * If NULL, use the default menu bar.
2031				 */
2032{
2033    TkWindow *winPtr = (TkWindow *) tkwin;
2034    WindowRef macWindowPtr;
2035    WindowRef frontNonFloating;
2036
2037    macWindowPtr = TkMacOSXDrawableWindow(winPtr->window);
2038
2039    frontNonFloating = ActiveNonFloatingWindow();
2040    if ((macWindowPtr == NULL) || (macWindowPtr != frontNonFloating)) {
2041	return;
2042    }
2043
2044    if ((currentMenuBarInterp != interp) || (currentMenuBarOwner != tkwin)
2045	    || (currentMenuBarName == NULL) || (menuName == NULL)
2046	    || (strcmp(menuName, currentMenuBarName) != 0)) {
2047	Tk_Window searchWindow;
2048	TopLevelMenubarList *listPtr;
2049
2050	if (currentMenuBarName != NULL) {
2051	    ckfree(currentMenuBarName);
2052	}
2053
2054	if (menuName == NULL) {
2055	    searchWindow = tkwin;
2056	    if (strcmp(Tk_Class(searchWindow), "Menu") == 0) {
2057		TkMenuReferences *menuRefPtr;
2058
2059		menuRefPtr = TkFindMenuReferences(interp, Tk_PathName(tkwin));
2060		if (menuRefPtr != NULL) {
2061		    TkMenu *menuPtr = menuRefPtr->menuPtr;
2062
2063		    if (menuPtr != NULL) {
2064			searchWindow = menuPtr->masterMenuPtr->tkwin;
2065		    }
2066		}
2067	    }
2068	    for (; searchWindow != NULL;
2069		    searchWindow = Tk_Parent(searchWindow)) {
2070		for (listPtr = windowListPtr; listPtr != NULL;
2071			listPtr = listPtr->nextPtr) {
2072		    if (listPtr->tkwin == searchWindow) {
2073			break;
2074		    }
2075		}
2076		if (listPtr != NULL) {
2077		    menuName = Tk_PathName(
2078			    listPtr->menuPtr->masterMenuPtr->tkwin);
2079		    break;
2080		}
2081	    }
2082	}
2083
2084	if (menuName == NULL) {
2085	    currentMenuBarName = NULL;
2086	} else {
2087	    currentMenuBarName = ckalloc(strlen(menuName) + 1);
2088	    strcpy(currentMenuBarName, menuName);
2089	}
2090	currentMenuBarOwner = tkwin;
2091	currentMenuBarInterp = interp;
2092    }
2093    if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) {
2094	Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL);
2095	menuBarFlags |= MENUBAR_REDRAW_PENDING;
2096    }
2097}
2098
2099/*
2100 *----------------------------------------------------------------------
2101 *
2102 * TkpSetWindowMenuBar --
2103 *
2104 *	Associates a given menu with a window.
2105 *
2106 * Results:
2107 *	None.
2108 *
2109 * Side effects:
2110 *	On Windows and UNIX, associates the platform menu with the
2111 *	platform window.
2112 *
2113 *----------------------------------------------------------------------
2114 */
2115
2116void
2117TkpSetWindowMenuBar(
2118    Tk_Window tkwin,		/* The window we are setting the menu in */
2119    TkMenu *menuPtr)		/* The menu we are setting */
2120{
2121    TopLevelMenubarList *listPtr, *prevPtr;
2122
2123    /*
2124     * Remove any existing reference to this window.
2125     */
2126
2127    for (prevPtr = NULL, listPtr = windowListPtr;
2128	    listPtr != NULL;
2129	    prevPtr = listPtr, listPtr = listPtr->nextPtr) {
2130	if (listPtr->tkwin == tkwin) {
2131	    break;
2132	}
2133    }
2134
2135    if (listPtr != NULL) {
2136	if (prevPtr != NULL) {
2137	    prevPtr->nextPtr = listPtr->nextPtr;
2138	} else {
2139	    windowListPtr = listPtr->nextPtr;
2140	}
2141	ckfree((char *) listPtr);
2142    }
2143
2144    if (menuPtr != NULL) {
2145	listPtr = (TopLevelMenubarList *) ckalloc(sizeof(TopLevelMenubarList));
2146	listPtr->nextPtr = windowListPtr;
2147	windowListPtr = listPtr;
2148	listPtr->tkwin = tkwin;
2149	listPtr->menuPtr = menuPtr;
2150    }
2151}
2152
2153/*
2154 *----------------------------------------------------------------------
2155 *
2156 * EventuallyInvokeMenu --
2157 *
2158 *	This IdleTime callback actually invokes the menu command
2159 *	scheduled in TkMacOSXDispatchMenuEvent.
2160 *
2161 * Results:
2162 *	None.
2163 *
2164 * Side effects:
2165 *	Commands get executed.
2166 *
2167 *----------------------------------------------------------------------
2168 */
2169
2170void
2171EventuallyInvokeMenu (
2172    ClientData data)
2173{
2174    struct MenuCommandHandlerData *realData =
2175	    (struct MenuCommandHandlerData *) data;
2176    int code;
2177
2178    code = TkInvokeMenu(realData->menuPtr->interp, realData->menuPtr,
2179	    realData->index);
2180
2181    if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) {
2182	Tcl_AddErrorInfo(realData->menuPtr->interp, "\n    (menu invoke)");
2183	Tcl_BackgroundError(realData->menuPtr->interp);
2184    }
2185
2186    if (realData->menuPtr->tkwin) {
2187	RecursivelyClearActiveMenu(realData->menuPtr);
2188    }
2189    TkMacOSXClearMenubarActive();
2190
2191    Tcl_Release(realData->menuPtr->interp);
2192    Tcl_Release(realData->menuPtr);
2193}
2194
2195/*
2196 *----------------------------------------------------------------------
2197 *
2198 * TkMacOSXDispatchMenuEvent --
2199 *
2200 *	Given a menu id and an item, dispatches the command associated
2201 *	with it.
2202 *
2203 * Results:
2204 *	None.
2205 *
2206 * Side effects:
2207 *	Commands for the event are scheduled for execution at idle time.
2208 *
2209 *----------------------------------------------------------------------
2210 */
2211
2212int
2213TkMacOSXDispatchMenuEvent(
2214    int menuID,			/* The menu id of the menu we are invoking */
2215    int index)			/* The one-based index of the item that was
2216				 * selected. */
2217{
2218    int result = TCL_OK;
2219
2220    if (menuID != 0) {
2221	if (menuID == kHMHelpMenuID) {
2222	    if (currentMenuBarOwner != NULL) {
2223		TkMenuReferences *helpMenuRef;
2224		char *helpMenuName = ckalloc(strlen(currentMenuBarName)
2225			+ strlen(".help") + 1);
2226
2227		sprintf(helpMenuName, "%s.help", currentMenuBarName);
2228		helpMenuRef = TkFindMenuReferences(currentMenuBarInterp,
2229			helpMenuName);
2230		ckfree(helpMenuName);
2231		if ((helpMenuRef != NULL) && (helpMenuRef->menuPtr != NULL)) {
2232		    MenuRef outHelpMenu;
2233		    MenuItemIndex itemIndex;
2234		    int newIndex;
2235
2236		    HMGetHelpMenu(&outHelpMenu, &itemIndex);
2237		    newIndex = index - itemIndex;
2238		    result = TkInvokeMenu(currentMenuBarInterp,
2239			    helpMenuRef->menuPtr, newIndex);
2240		}
2241	    }
2242	} else {
2243	    Tcl_HashEntry *commandEntryPtr =
2244		    Tcl_FindHashEntry(&commandTable, (char*)(intptr_t)menuID);
2245	    if (commandEntryPtr != NULL) {
2246		TkMenu *menuPtr = (TkMenu *) Tcl_GetHashValue(commandEntryPtr);
2247
2248		if ((currentAppleMenuID == menuID)
2249			&& (index > menuPtr->numEntries + 1)) {
2250		    /*
2251		     * We don't need to do anything here, the standard
2252		     * Application event handler will open the built-in
2253		     * Apple menu item for us.
2254		     */
2255		    result = TCL_OK;
2256		} else {
2257		    struct MenuCommandHandlerData *data
2258			    = (struct MenuCommandHandlerData *)
2259			    ckalloc(sizeof(struct MenuCommandHandlerData));
2260
2261		    Tcl_Preserve(menuPtr->interp);
2262		    Tcl_Preserve(menuPtr);
2263		    data->menuPtr = menuPtr;
2264		    data->index = index - 1;
2265		    Tcl_DoWhenIdle(EventuallyInvokeMenu,
2266			    (ClientData) data);
2267		    /* result = TkInvokeMenu(menuPtr->interp, menuPtr, index - 1); */
2268		}
2269	    } else {
2270		return TCL_ERROR;
2271	    }
2272	}
2273    }
2274    return result;
2275}
2276
2277/*
2278 *----------------------------------------------------------------------
2279 *
2280 * GetMenuIndicatorGeometry --
2281 *
2282 *	Gets the width and height of the indicator area of a menu.
2283 *
2284 * Results:
2285 *	widthPtr and heightPtr are set.
2286 *
2287 * Side effects:
2288 *	None.
2289 *
2290 *----------------------------------------------------------------------
2291 */
2292
2293void
2294GetMenuIndicatorGeometry (
2295    TkMenu *menuPtr,		/* The menu we are drawing */
2296    TkMenuEntry *mePtr,		/* The entry we are measuring */
2297    Tk_Font tkfont,		/* Precalculated font */
2298    const Tk_FontMetrics *fmPtr,/* Precalculated font metrics */
2299    int *widthPtr,		/* The resulting width */
2300    int *heightPtr)		/* The resulting height */
2301{
2302    *heightPtr = fmPtr->linespace + menuItemExtraHeight;
2303    if (IS_THEME_MENU_FONT(tkfont)) {
2304	*widthPtr = menuMarkColumnWidth;
2305    } else {
2306	const char markChar = FindMarkCharacter(mePtr);
2307	const char *markUtf = NULL;
2308	int len;
2309
2310	len = GetUtfMarkCharacter(markChar, &markUtf);
2311	*widthPtr = Tk_TextWidth(tkfont, markUtf, len) + 2*menuMarkIndent;
2312    }
2313}
2314
2315/*
2316 *----------------------------------------------------------------------
2317 *
2318 * GetMenuAccelGeometry --
2319 *
2320 *	Gets the width and height of the accelerator area of a menu.
2321 *
2322 * Results:
2323 *	widthPtr and heightPtr are set.
2324 *
2325 * Side effects:
2326 *	None.
2327 *
2328 *----------------------------------------------------------------------
2329 */
2330
2331void
2332GetMenuAccelGeometry (
2333    TkMenu *menuPtr,		/* The menu we are measuring */
2334    TkMenuEntry *mePtr,		/* The entry we are measuring */
2335    Tk_Font tkfont,		/* The precalculated font */
2336    const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */
2337    int *modWidthPtr,		/* The width of all of the key
2338				 * modifier symbols. */
2339    int *textWidthPtr,		/* The resulting width */
2340    int *heightPtr)		/* The resulting height */
2341{
2342    *heightPtr = fmPtr->linespace + menuItemExtraHeight;
2343    *modWidthPtr = menuSymbols[COMMAND_SYMBOL].width;
2344    *textWidthPtr = 0;
2345    if (mePtr->type != CASCADE_ENTRY && mePtr->accelLength > 0) {
2346	const char *accel = (mePtr->accelPtr == NULL) ? ""
2347		: Tcl_GetString(mePtr->accelPtr);
2348	EntryGeometry *geometryPtr = (EntryGeometry*)mePtr->platformEntryData;
2349
2350	if (IS_THEME_MENU_FONT(tkfont)) {
2351	    CFStringRef cfStr;
2352	    int width = 0;
2353	    int maxWidth = ((TkFont *)tkfont)->fm.maxWidth;
2354
2355	    if (geometryPtr->accelGlyph) {
2356		cfStr = CFStringCreateWithBytes(NULL,
2357			(UInt8*)&geometryPtr->accelGlyph, 1,
2358			kTextEncodingMacKeyboardGlyphs, false);
2359		if (cfStr) {
2360		    width = MeasureThemeText(cfStr, kThemeMenuItemCmdKeyFont);
2361		    CFRelease(cfStr);
2362		}
2363	    }
2364	    if ((mePtr->entryFlags & ENTRY_ACCEL_MASK) == 0) {
2365		if (!geometryPtr->accelGlyph) {
2366		     width = Tk_TextWidth(tkfont, accel, mePtr->accelLength);
2367		 }
2368		*textWidthPtr = maxWidth;
2369		if (width < maxWidth) {
2370		    *modWidthPtr = 0;
2371		} else {
2372		    *modWidthPtr = width - maxWidth;
2373		}
2374	    } else {
2375		if (!geometryPtr->accelGlyph) {
2376		    width = Tk_TextWidth(tkfont, accel +
2377			    geometryPtr->accelTextStart, mePtr->accelLength -
2378			    geometryPtr->accelTextStart);
2379		}
2380		if (width < maxWidth) {
2381		    *textWidthPtr = maxWidth;
2382		} else {
2383		    *textWidthPtr = width;
2384		}
2385		if (geometryPtr->modifierNum) {
2386		    *modWidthPtr = geometryPtr->modifierWidth;
2387		}
2388	    }
2389	} else {
2390	    *textWidthPtr = Tk_TextWidth(tkfont, accel, mePtr->accelLength);
2391	}
2392    }
2393}
2394
2395/*
2396 *----------------------------------------------------------------------
2397 *
2398 * GetTearoffEntryGeometry --
2399 *
2400 *	Gets the width and height of of a tearoff entry.
2401 *
2402 * Results:
2403 *	widthPtr and heightPtr are set.
2404 *
2405 * Side effects:
2406 *	None.
2407 *
2408 *----------------------------------------------------------------------
2409 */
2410
2411void
2412GetTearoffEntryGeometry (
2413    TkMenu *menuPtr,		/* The menu we are drawing */
2414    TkMenuEntry *mePtr,		/* The entry we are measuring */
2415    Tk_Font tkfont,		/* The precalculated font */
2416    const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */
2417    int *widthPtr,		/* The resulting width */
2418    int *heightPtr)		/* The resulting height */
2419{
2420#ifdef USE_TK_MDEF
2421    const int useMDEF = ((MacMenu *) menuPtr->platformData)->useMDEF;
2422#endif
2423    if (useMDEF && menuPtr->menuType != TEAROFF_MENU) {
2424	*heightPtr = fmPtr->linespace + menuItemExtraHeight;
2425	*widthPtr = menuPtr->totalWidth;
2426    } else {
2427	*widthPtr = *heightPtr = 0;
2428    }
2429}
2430
2431/*
2432 *----------------------------------------------------------------------
2433 *
2434 * GetMenuSeparatorGeometry --
2435 *
2436 *	Gets the width and height of menu separator.
2437 *
2438 * Results:
2439 *	widthPtr and heightPtr are set.
2440 *
2441 * Side effects:
2442 *	None.
2443 *
2444 *----------------------------------------------------------------------
2445 */
2446
2447void
2448GetMenuSeparatorGeometry(
2449    TkMenu *menuPtr,		/* The menu we are drawing */
2450    TkMenuEntry *mePtr,		/* The entry we are measuring */
2451    Tk_Font tkfont,		/* The precalculated font */
2452    const Tk_FontMetrics *fmPtr,/* The precalcualted font metrics */
2453    int *widthPtr,		/* The resulting width */
2454    int *heightPtr)		/* The resulting height */
2455{
2456    *widthPtr = 0;
2457    *heightPtr = menuSeparatorHeight;
2458}
2459
2460/*
2461 *----------------------------------------------------------------------
2462 *
2463 * DrawMenuEntryIndicator --
2464 *
2465 *	This procedure draws the indicator part of a menu.
2466 *
2467 * Results:
2468 *	None.
2469 *
2470 * Side effects:
2471 *	Commands are output to X to display the menu in its
2472 *	current mode.
2473 *
2474 *----------------------------------------------------------------------
2475 */
2476
2477void
2478DrawMenuEntryIndicator(
2479    TkMenu *menuPtr,		/* The menu we are drawing */
2480    TkMenuEntry *mePtr,		/* The entry we are drawing */
2481    Drawable d,			/* The drawable we are drawing */
2482    GC gc,			/* The GC we are drawing with */
2483    GC indicatorGC,		/* The GC to use for the indicator */
2484    Tk_Font tkfont,		/* The precalculated font */
2485    const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */
2486    int x,			/* topleft hand corner of entry */
2487    int y,			/* topleft hand corner of entry */
2488    int width,			/* width of entry */
2489    int height)			/* height of entry */
2490{
2491    if ((mePtr->type == CHECK_BUTTON_ENTRY) ||
2492	    (mePtr->type == RADIO_BUTTON_ENTRY)) {
2493	if (mePtr->indicatorOn && (mePtr->entryFlags & ENTRY_SELECTED)) {
2494	    short mark;
2495	    int baseline = y + (height + fmPtr->ascent - fmPtr->descent)/2;
2496
2497	    GetItemMark(((MacMenu *) menuPtr->platformData)->menuHdl,
2498		    mePtr->index + 1, &mark);
2499	    if (IS_THEME_MENU_FONT(tkfont)) {
2500		ThemeFontID font = kThemeMenuItemMarkFont;
2501		TextEncoding encoding = GetApplicationTextEncoding();
2502		CFStringRef cfStr;
2503		ThemeDrawState drawState;
2504		Rect bounds = {y, x + menuMarkIndent, y + height, x + width};
2505
2506		if (mark < kSpaceCharCode) {
2507		    font = kThemeMenuItemCmdKeyFont;
2508		    encoding = kTextEncodingMacKeyboardGlyphs;
2509		}
2510		switch (mePtr->state) {
2511		    case ENTRY_ACTIVE:
2512			drawState = kThemeStatePressed;
2513			break;
2514		    case ENTRY_DISABLED:
2515			drawState = kThemeStateInactive;
2516			break;
2517		    default:
2518			drawState = kThemeStateActive;
2519			break;
2520		}
2521		cfStr = CFStringCreateWithBytes(NULL, (UInt8*)&mark, 1,
2522			encoding, false);
2523		if (cfStr) {
2524		    DrawThemeText(d, gc, cfStr, font, drawState, &bounds,
2525			    baseline, teFlushDefault);
2526		    CFRelease(cfStr);
2527		}
2528	    } else if (mark != 0) {
2529		const char *markUtf = NULL;
2530		int len;
2531
2532		len = GetUtfMarkCharacter(mark, &markUtf);
2533		Tk_DrawChars(menuPtr->display, d, gc, tkfont, markUtf, len,
2534		    x + menuMarkIndent, baseline);
2535	    }
2536	}
2537    }
2538}
2539
2540#ifdef USE_TK_MDEF
2541/*
2542 *----------------------------------------------------------------------
2543 *
2544 * DrawMenuBackground --
2545 *
2546 *	If Appearance is present, draws the Appearance background
2547 *
2548 * Results:
2549 *	Nothing
2550 *
2551 * Side effects:
2552 *	Commands are output to X to display the menu in its
2553 *	current mode.
2554 *
2555 *----------------------------------------------------------------------
2556 */
2557void
2558DrawMenuBackground(
2559    TkMenu *menuPtr,
2560    Rect     *menuRectPtr,	/* The menu rect */
2561    Drawable d)			/* What we are drawing into */
2562{
2563    Tk_3DBorder border;
2564
2565    EraseMenuBackground(((MacMenu *) menuPtr->platformData)->menuHdl,
2566	    menuRectPtr, ((MacDrawable*)d)->context);
2567    border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
2568    Tk_Fill3DRectangle(menuPtr->tkwin, d, border,
2569	    menuRectPtr->left, menuRectPtr->top,
2570	    menuRectPtr->right - menuRectPtr->left,
2571	    menuRectPtr->bottom - menuRectPtr->top, 0, TK_RELIEF_FLAT);
2572}
2573#endif /* USE_TK_MDEF */
2574
2575/*
2576 *----------------------------------------------------------------------
2577 *
2578 * DrawMenuEntryAccelerator --
2579 *
2580 *	This procedure draws the accelerator part of a menu.
2581 *
2582 * Results:
2583 *	None.
2584 *
2585 * Side effects:
2586 *	Commands are output to X to display the menu in its
2587 *	current mode.
2588 *
2589 *----------------------------------------------------------------------
2590 */
2591
2592void
2593DrawMenuEntryAccelerator(
2594    TkMenu *menuPtr,		/* The menu we are drawing */
2595    TkMenuEntry *mePtr,		/* The entry we are drawing */
2596    Drawable d,			/* The drawable we are drawing in */
2597    GC gc,			/* The gc to draw into */
2598    Tk_Font tkfont,		/* The precalculated font */
2599    const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */
2600    Tk_3DBorder activeBorder,	/* border for menu background */
2601    int x,			/* The left side of the entry */
2602    int y,			/* The top of the entry */
2603    int width,			/* The width of the entry */
2604    int height,			/* The height of the entry */
2605    int drawArrow)		/* Whether or not to draw cascade arrow */
2606{
2607    if (mePtr->type != CASCADE_ENTRY && mePtr->accelLength > 0) {
2608	const char *accel = (mePtr->accelPtr == NULL) ? ""
2609		: Tcl_GetString(mePtr->accelPtr);
2610	EntryGeometry *geometryPtr = (EntryGeometry*)mePtr->platformEntryData;
2611	int leftEdge = x + width - geometryPtr->accelTextWidth;
2612	int baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
2613
2614	if (IS_THEME_MENU_FONT(tkfont)) {
2615	    CFStringRef cfStr;
2616	    ThemeDrawState drawState;
2617
2618	    switch (mePtr->state) {
2619		case ENTRY_ACTIVE:
2620		    drawState = kThemeStatePressed;
2621		    break;
2622		case ENTRY_DISABLED:
2623		    drawState = kThemeStateInactive;
2624		    break;
2625		default:
2626		    drawState = kThemeStateActive;
2627		    break;
2628	    }
2629	    if ((mePtr->entryFlags & ENTRY_ACCEL_MASK) == 0) {
2630		leftEdge -= geometryPtr->modifierWidth;
2631	    }
2632	    if (geometryPtr->accelGlyph) {
2633		Rect bounds = {y, leftEdge, y + height, leftEdge +
2634			geometryPtr->accelTextWidth};
2635
2636		cfStr = CFStringCreateWithBytes(NULL,
2637			(UInt8*)&geometryPtr->accelGlyph, 1,
2638			kTextEncodingMacKeyboardGlyphs, false);
2639		if (cfStr) {
2640		    DrawThemeText(d, gc, cfStr, kThemeMenuItemCmdKeyFont,
2641			    drawState, &bounds, baseline, teFlushDefault);
2642		    CFRelease(cfStr);
2643		}
2644	    } else {
2645		Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel +
2646			geometryPtr->accelTextStart, mePtr->accelLength -
2647			geometryPtr->accelTextStart, leftEdge, baseline);
2648	    }
2649	    if (geometryPtr->modifierNum) {
2650		Rect bounds = {y, leftEdge - geometryPtr->modifierWidth,
2651			y + height, leftEdge};
2652
2653		cfStr = CFStringCreateWithCharacters(NULL,
2654			geometryPtr->modifierUniChars,
2655			geometryPtr->modifierNum);
2656		if (cfStr) {
2657		    DrawThemeText(d, gc, cfStr, kThemeMenuItemCmdKeyFont,
2658			    drawState, &bounds, baseline, teFlushDefault);
2659		    CFRelease(cfStr);
2660		}
2661	    }
2662	} else {
2663	    Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel,
2664		    mePtr->accelLength, leftEdge, baseline);
2665	}
2666    }
2667}
2668
2669/*
2670 *----------------------------------------------------------------------
2671 *
2672 * DrawMenuSeparator --
2673 *
2674 *	The menu separator is drawn.
2675 *
2676 * Results:
2677 *	None.
2678 *
2679 * Side effects:
2680 *	Commands are output to X to display the menu in its
2681 *	current mode.
2682 *
2683 *----------------------------------------------------------------------
2684 */
2685
2686void
2687DrawMenuSeparator(
2688    TkMenu *menuPtr,		/* The menu we are drawing */
2689    TkMenuEntry *mePtr,		/* The entry we are drawing */
2690    Drawable d,			/* The drawable we are drawing into */
2691    GC gc,			/* The gc we are drawing with */
2692    Tk_Font tkfont,		/* The precalculated font */
2693    const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */
2694    int x,			/* left coordinate of entry */
2695    int y,			/* top coordinate of entry */
2696    int width,			/* width of entry */
2697    int height)			/* height of entry */
2698{
2699    TkMacOSXDrawingContext dc;
2700    Rect r;
2701
2702    r.top = y;
2703    r.left = x;
2704    r.bottom = y + height;
2705    r.right = x + width;
2706    if (TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) {
2707	ChkErr(DrawThemeMenuSeparator, &r);
2708	TkMacOSXRestoreDrawingContext(&dc);
2709    }
2710}
2711
2712#ifdef USE_TK_MDEF
2713/*
2714 *----------------------------------------------------------------------
2715 *
2716 * AppearanceEntryDrawWrapper --
2717 *
2718 *	It routes to the Appearance Managers DrawThemeEntry, which will
2719 *	then call us back after setting up the drawing context.
2720 *
2721 * Results:
2722 *	A menu entry is drawn
2723 *
2724 * Side effects:
2725 *	None
2726 *
2727 *----------------------------------------------------------------------
2728 */
2729void
2730AppearanceEntryDrawWrapper(
2731    TkMenuEntry *mePtr,
2732    Rect *menuRectPtr,
2733    MenuTrackingData *mtdPtr,
2734    Drawable d,
2735    Tk_FontMetrics *fmPtr,
2736    Tk_Font tkfont,
2737    int erase)
2738{
2739    MenuEntryUserData meData;
2740    Rect itemRect;
2741    ThemeMenuState theState;
2742    ThemeMenuItemType theType;
2743    Tk_FontMetrics entryMetrics;
2744
2745    meData.mePtr = mePtr;
2746    meData.mdefDrawable = d;
2747    if (mePtr->fontPtr == NULL) {
2748	meData.fmPtr = fmPtr;
2749	meData.tkfont = tkfont;
2750    } else {
2751	meData.tkfont = Tk_GetFontFromObj(mePtr->menuPtr->tkwin,
2752		mePtr->fontPtr);
2753	Tk_GetFontMetrics(meData.tkfont, &entryMetrics);
2754	fmPtr = &entryMetrics;
2755    }
2756    itemRect.left = menuRectPtr->left + mePtr->x;
2757    itemRect.top = mtdPtr->virtualMenuTop + mePtr->y;
2758    itemRect.right = mePtr->entryFlags & ENTRY_LAST_COLUMN ?
2759	    menuRectPtr->right : itemRect.left + mePtr->width;
2760    itemRect.bottom = itemRect.top + mePtr->height;
2761
2762    if (mePtr->state == ENTRY_ACTIVE) {
2763	theState = kThemeMenuSelected;
2764    } else if (mePtr->state == ENTRY_DISABLED) {
2765	theState = kThemeMenuDisabled;
2766    } else {
2767	theState = kThemeMenuActive;
2768    }
2769    if (mePtr->type == CASCADE_ENTRY) {
2770	theType = kThemeMenuItemHierarchical;
2771    } else {
2772	theType = kThemeMenuItemPlain;
2773    }
2774    if (erase) {
2775	DisableScreenUpdates();
2776	DrawMenuBackground(mePtr->menuPtr, &itemRect, d);
2777    }
2778    DrawThemeMenuItem(menuRectPtr, &itemRect,
2779	mtdPtr->virtualMenuTop, mtdPtr->virtualMenuBottom, theState,
2780	theType | kThemeMenuItemNoBackground, tkThemeMenuItemDrawingUPP,
2781	(unsigned long) &meData);
2782    if (erase) {
2783	EnableScreenUpdates();
2784    }
2785}
2786
2787/*
2788 *----------------------------------------------------------------------
2789 *
2790 * ThemeMenuItemDrawingProc --
2791 *
2792 *	This routine is called from the Appearance DrawThemeMenuEntry
2793 *
2794 * Results:
2795 *	A menu entry is drawn
2796 *
2797 * Side effects:
2798 *	None
2799 *
2800 *----------------------------------------------------------------------
2801 */
2802pascal void
2803ThemeMenuItemDrawingProc(
2804    const Rect *inBounds,
2805    SInt16 inDepth,
2806    Boolean inIsColorDevice,
2807    SInt32 inUserData)
2808{
2809    MenuEntryUserData *meData = (MenuEntryUserData *) inUserData;
2810
2811    TkpDrawMenuEntry(meData->mePtr, meData->mdefDrawable, meData->tkfont,
2812	    meData->fmPtr, inBounds->left, inBounds->top, inBounds->right -
2813	    inBounds->left + menuItemExtraWidth, inBounds->bottom -
2814	    inBounds->top + menuItemExtraHeight, 0, 1);
2815}
2816#endif /* USE_TK_MDEF */
2817
2818/*
2819 *----------------------------------------------------------------------
2820 *
2821 * TkMacOSXHandleTearoffMenu() --
2822 *
2823 *	This routine sees if the MDEF has set a menu and a mouse position
2824 *	for tearing off and makes a tearoff menu if it has.
2825 *
2826 * Results:
2827 *	menuPtr->interp will have the result of the tearoff command.
2828 *
2829 * Side effects:
2830 *	A new tearoff menu is created if it is supposed to be.
2831 *
2832 *----------------------------------------------------------------------
2833 */
2834
2835void
2836TkMacOSXHandleTearoffMenu(void)
2837{
2838    /*
2839     * Obsolete: Nothing to do.
2840     */
2841}
2842
2843/*
2844 *--------------------------------------------------------------
2845 *
2846 * TkpInitializeMenuBindings --
2847 *
2848 *	For every interp, initializes the bindings for Windows
2849 *	menus. Does nothing on Mac or XWindows.
2850 *
2851 * Results:
2852 *	None.
2853 *
2854 * Side effects:
2855 *	C-level bindings are setup for the interp which will
2856 *	handle Alt-key sequences for menus without beeping
2857 *	or interfering with user-defined Alt-key bindings.
2858 *
2859 *--------------------------------------------------------------
2860 */
2861
2862void
2863TkpInitializeMenuBindings(
2864    Tcl_Interp *interp,		/* The interpreter to set. */
2865    Tk_BindingTable bindingTable)
2866				/* The table to add to. */
2867{
2868    /*
2869     * Nothing to do.
2870     */
2871}
2872
2873/*
2874 *--------------------------------------------------------------
2875 *
2876 * TkpComputeMenubarGeometry --
2877 *
2878 *	This procedure is invoked to recompute the size and
2879 *	layout of a menu that is a menubar clone.
2880 *
2881 * Results:
2882 *	None.
2883 *
2884 * Side effects:
2885 *	Fields of menu entries are changed to reflect their
2886 *	current positions, and the size of the menu window
2887 *	itself may be changed.
2888 *
2889 *--------------------------------------------------------------
2890 */
2891
2892void
2893TkpComputeMenubarGeometry(
2894    TkMenu *menuPtr)		/* Structure describing menu. */
2895{
2896    TkpComputeStandardMenuGeometry(menuPtr);
2897}
2898
2899/*
2900 *----------------------------------------------------------------------
2901 *
2902 * DrawTearoffEntry --
2903 *
2904 *	This procedure draws a tearoff entry.
2905 *
2906 * Results:
2907 *	None.
2908 *
2909 * Side effects:
2910 *	Commands are output to X to display the menu in its
2911 *	current mode.
2912 *
2913 *----------------------------------------------------------------------
2914 */
2915
2916void
2917DrawTearoffEntry(
2918    TkMenu *menuPtr,		/* The menu we are drawing */
2919    TkMenuEntry *mePtr,		/* The entry we are drawing */
2920    Drawable d,			/* The drawable we are drawing into */
2921    GC gc,			/* The gc we are drawing with */
2922    Tk_Font tkfont,		/* The font we are drawing with */
2923    const Tk_FontMetrics *fmPtr,/* The metrics we are drawing with */
2924    int x,			/* Left edge of entry. */
2925    int y,			/* Top edge of entry. */
2926    int width,			/* Width of entry. */
2927    int height)			/* Height of entry. */
2928{
2929    XPoint points[2];
2930    int margin, segmentWidth, maxX;
2931    Tk_3DBorder border;
2932
2933    if (menuPtr->menuType != MASTER_MENU ) {
2934	return;
2935    }
2936
2937    margin = fmPtr->linespace/2;
2938    points[0].x = x;
2939    points[0].y = y + height/2;
2940    points[1].y = points[0].y;
2941    segmentWidth = 6;
2942    maxX  = x + menuPtr->totalWidth - 1;
2943    border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
2944
2945    while (points[0].x < maxX) {
2946	points[1].x = points[0].x + segmentWidth;
2947	if (points[1].x > maxX) {
2948	    points[1].x = maxX;
2949	}
2950	Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1,
2951		TK_RELIEF_RAISED);
2952	points[0].x += 2*segmentWidth;
2953    }
2954}
2955
2956/*
2957 *----------------------------------------------------------------------
2958 *
2959 * TkMacOSXSetHelpMenuItemCount --
2960 *
2961 *	Has to be called after the first call to InsertMenu. Sets
2962 *	up the global variable for the number of items in the
2963 *	unmodified help menu.
2964 *	NB. Nobody uses this any more, since you can get the number
2965 *	of system help items from HMGetHelpMenu trivially.
2966 *	But it is in the stubs table...
2967 *
2968 * Results:
2969 *	None.
2970 *
2971 * Side effects:
2972 *	Nothing.
2973 *
2974 *----------------------------------------------------------------------
2975 */
2976
2977void
2978TkMacOSXSetHelpMenuItemCount(void)
2979{
2980    /*
2981     * Obsolete: Nothing to do.
2982     */
2983}
2984
2985/*
2986 *----------------------------------------------------------------------
2987 *
2988 * TkMacOSXMenuClick --
2989 *
2990 *	Prepares a menubar for MenuSelect or MenuKey.
2991 *
2992 * Results:
2993 *	None.
2994 *
2995 * Side effects:
2996 *	Any pending configurations of the menubar are completed.
2997 *
2998 *----------------------------------------------------------------------
2999 */
3000
3001void
3002TkMacOSXMenuClick(void)
3003{
3004    TkMenu *menuPtr;
3005    TkMenuReferences *menuRefPtr;
3006
3007    if ((currentMenuBarInterp != NULL) && (currentMenuBarName != NULL)) {
3008	menuRefPtr = TkFindMenuReferences(currentMenuBarInterp,
3009		currentMenuBarName);
3010	for (menuPtr = menuRefPtr->menuPtr->masterMenuPtr;
3011		menuPtr != NULL; menuPtr = menuPtr->nextInstancePtr) {
3012	    if (menuPtr->menuType == MENUBAR) {
3013		CompleteIdlers(menuPtr);
3014		break;
3015	    }
3016	}
3017    }
3018
3019    if (menuBarFlags & MENUBAR_REDRAW_PENDING) {
3020	Tcl_CancelIdleCall(DrawMenuBarWhenIdle, NULL);
3021	DrawMenuBarWhenIdle(NULL);
3022    }
3023}
3024
3025/*
3026 *----------------------------------------------------------------------
3027 *
3028 * TkpDrawMenuEntry --
3029 *
3030 *	Draws the given menu entry at the given coordinates with the
3031 *	given attributes.
3032 *
3033 * Results:
3034 *	None.
3035 *
3036 * Side effects:
3037 *	X Server commands are executed to display the menu entry.
3038 *
3039 *----------------------------------------------------------------------
3040 */
3041
3042void
3043TkpDrawMenuEntry(
3044    TkMenuEntry *mePtr,		/* The entry to draw */
3045    Drawable d,			/* What to draw into */
3046    Tk_Font tkfont,		/* Precalculated font for menu */
3047    const Tk_FontMetrics *menuMetricsPtr,
3048				/* Precalculated metrics for menu */
3049    int x,			/* X-coordinate of topleft of entry */
3050    int y,			/* Y-coordinate of topleft of entry */
3051    int width,			/* Width of the entry rectangle */
3052    int height,			/* Height of the current rectangle */
3053    int strictMotif,		/* Boolean flag */
3054    int drawArrow)		/* Whether or not to draw the cascade
3055				 * arrow for cascade items. Only applies
3056				 * to Windows. */
3057{
3058    GC gc;
3059    TkMenu *menuPtr = mePtr->menuPtr;
3060    int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0;
3061    GC indicatorGC;
3062    Tk_3DBorder bgBorder, activeBorder;
3063    const Tk_FontMetrics *fmPtr;
3064    Tk_FontMetrics entryMetrics;
3065    int adjustedY = y + padY;
3066    int adjustedHeight = height - 2 * padY;
3067
3068    /*
3069     * Choose the gc for drawing the foreground part of the entry.
3070     * Under Appearance, we pass a null (appearanceGC) to tell
3071     * ourselves not to change whatever color the appearance manager has set.
3072     */
3073
3074    if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) {
3075	gc = mePtr->activeGC;
3076	if (gc == NULL) {
3077	    gc = menuPtr->activeGC;
3078	}
3079    } else {
3080	TkMenuEntry *parentEntryPtr = GetParentMenuEntry(menuPtr);
3081
3082	if (((parentEntryPtr && parentEntryPtr->state == ENTRY_DISABLED) ||
3083		(mePtr->state == ENTRY_DISABLED)) &&
3084		(menuPtr->disabledFgPtr != NULL)) {
3085	    gc = mePtr->disabledGC;
3086	    if (gc == NULL) {
3087		gc = menuPtr->disabledGC;
3088	    }
3089	} else {
3090	    gc = mePtr->textGC;
3091	    if (gc == NULL) {
3092		gc = menuPtr->textGC;
3093	    }
3094	}
3095    }
3096
3097    indicatorGC = mePtr->indicatorGC;
3098    if (indicatorGC == NULL) {
3099	indicatorGC = menuPtr->indicatorGC;
3100    }
3101
3102    bgBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
3103	    (mePtr->borderPtr == NULL)
3104	    ? menuPtr->borderPtr : mePtr->borderPtr);
3105    if (strictMotif) {
3106	activeBorder = bgBorder;
3107    } else {
3108	activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
3109	    (mePtr->activeBorderPtr == NULL)
3110	    ? menuPtr->activeBorderPtr : mePtr->activeBorderPtr);
3111    }
3112
3113    if (mePtr->fontPtr == NULL) {
3114	fmPtr = menuMetricsPtr;
3115    } else {
3116	tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
3117	Tk_GetFontMetrics(tkfont, &entryMetrics);
3118	fmPtr = &entryMetrics;
3119    }
3120
3121    /*
3122     * Need to draw the entire background, including padding. On Unix,
3123     * for menubars, we have to draw the rest of the entry taking
3124     * into account the padding.
3125     */
3126
3127    DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder, bgBorder, x, y,
3128	    width, height);
3129
3130    if (mePtr->type == SEPARATOR_ENTRY) {
3131	DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont,
3132		fmPtr, x, adjustedY, width, adjustedHeight);
3133    } else if (mePtr->type == TEAROFF_ENTRY) {
3134	DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
3135		width, adjustedHeight);
3136    } else {
3137	DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x,
3138		adjustedY, width, adjustedHeight);
3139	DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
3140		activeBorder, x, adjustedY, width, adjustedHeight, drawArrow);
3141	if (!mePtr->hideMargin) {
3142	    DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont,
3143		    fmPtr, x, adjustedY, width, adjustedHeight);
3144	}
3145    }
3146}
3147
3148/*
3149 *--------------------------------------------------------------
3150 *
3151 * TkpComputeStandardMenuGeometry --
3152 *
3153 *	This procedure is invoked to recompute the size and
3154 *	layout of a menu that is not a menubar clone.
3155 *
3156 * Results:
3157 *	None.
3158 *
3159 * Side effects:
3160 *	Fields of menu entries are changed to reflect their
3161 *	current positions, and the size of the menu window
3162 *	itself may be changed.
3163 *
3164 *--------------------------------------------------------------
3165 */
3166
3167void
3168TkpComputeStandardMenuGeometry(
3169    TkMenu *menuPtr)		/* Structure describing menu. */
3170{
3171    Tk_Font tkfont, menuFont;
3172    Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
3173    int x, y, height, modifierWidth, labelWidth, indicatorSpace;
3174    int windowWidth, windowHeight, accelWidth, maxAccelTextWidth;
3175    int i, j, lastColumnBreak, maxModifierWidth, maxWidth, nonAccelMargin;
3176    int maxNonAccelMargin, maxEntryWithAccelWidth, maxEntryWithoutAccelWidth;
3177    int entryWidth, maxIndicatorSpace, borderWidth, activeBorderWidth;
3178    TkMenuEntry *mePtr, *columnEntryPtr;
3179    EntryGeometry *geometryPtr;
3180    int haveAccel = 0;
3181
3182    if (menuPtr->tkwin == NULL) {
3183	return;
3184    }
3185
3186    Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
3187	    &borderWidth);
3188    Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
3189	    &activeBorderWidth);
3190    x = y = borderWidth;
3191    indicatorSpace = labelWidth = accelWidth = maxAccelTextWidth = 0;
3192    windowHeight = windowWidth = maxWidth = lastColumnBreak = 0;
3193    maxModifierWidth = nonAccelMargin = maxNonAccelMargin = 0;
3194    maxEntryWithAccelWidth = maxEntryWithoutAccelWidth = 0;
3195    maxIndicatorSpace = 0;
3196
3197    /*
3198     * On the Mac especially, getting font metrics can be quite slow,
3199     * so we want to do it intelligently. We are going to precalculate
3200     * them and pass them down to all of the measuring and drawing
3201     * routines. We will measure the font metrics of the menu once.
3202     * If an entry does not have its own font set, then we give
3203     * the geometry/drawing routines the menu's font and metrics.
3204     * If an entry has its own font, we will measure that font and
3205     * give all of the geometry/drawing the entry's font and metrics.
3206     */
3207
3208    menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
3209    Tk_GetFontMetrics(menuFont, &menuMetrics);
3210
3211    for (i = 0; i < menuPtr->numEntries; i++) {
3212	mePtr = menuPtr->entries[i];
3213	if (mePtr->type == CASCADE_ENTRY || mePtr->accelLength > 0) {
3214	    haveAccel = 1;
3215	    break;
3216	}
3217    }
3218
3219    for (i = 0; i < menuPtr->numEntries; i++) {
3220	mePtr = menuPtr->entries[i];
3221	if (mePtr->fontPtr == NULL) {
3222	    tkfont = menuFont;
3223	    fmPtr = &menuMetrics;
3224	} else {
3225	    tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
3226	    Tk_GetFontMetrics(tkfont, &entryMetrics);
3227	    fmPtr = &entryMetrics;
3228	}
3229
3230	if ((i > 0) && mePtr->columnBreak) {
3231	    if (maxIndicatorSpace != 0) {
3232		maxIndicatorSpace += 2;
3233	    }
3234	    for (j = lastColumnBreak; j < i; j++) {
3235		columnEntryPtr = menuPtr->entries[j];
3236		geometryPtr =
3237			(EntryGeometry *) columnEntryPtr->platformEntryData;
3238
3239		columnEntryPtr->indicatorSpace = maxIndicatorSpace;
3240		columnEntryPtr->width = maxIndicatorSpace + maxWidth
3241			+ 2 * activeBorderWidth;
3242		geometryPtr->accelTextWidth = maxAccelTextWidth;
3243		geometryPtr->modifierWidth = maxModifierWidth;
3244		columnEntryPtr->x = x;
3245		columnEntryPtr->entryFlags &= ~ENTRY_LAST_COLUMN;
3246		if (maxEntryWithoutAccelWidth > maxEntryWithAccelWidth) {
3247		    geometryPtr->nonAccelMargin = maxEntryWithoutAccelWidth
3248			    - maxEntryWithAccelWidth;
3249		    if (geometryPtr->nonAccelMargin > maxNonAccelMargin) {
3250			geometryPtr->nonAccelMargin = maxNonAccelMargin;
3251		    }
3252		} else {
3253		    geometryPtr->nonAccelMargin = 0;
3254		}
3255	    }
3256	    x += maxIndicatorSpace + maxWidth + 2 * borderWidth;
3257	    windowWidth = x;
3258	    maxWidth = maxIndicatorSpace = maxAccelTextWidth = 0;
3259	    maxModifierWidth = maxNonAccelMargin = maxEntryWithAccelWidth = 0;
3260	    maxEntryWithoutAccelWidth = 0;
3261	    lastColumnBreak = i;
3262	    y = borderWidth;
3263	}
3264	geometryPtr = (EntryGeometry *) mePtr->platformEntryData;
3265
3266	if (mePtr->type == SEPARATOR_ENTRY) {
3267	    GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont,
3268		    fmPtr, &entryWidth, &height);
3269	    mePtr->height = height;
3270	} else if (mePtr->type == TEAROFF_ENTRY) {
3271	    GetTearoffEntryGeometry(menuPtr, mePtr, tkfont,
3272		    fmPtr, &entryWidth, &height);
3273	    mePtr->height = height;
3274	} else {
3275	    /*
3276	     * For each entry, compute the height required by that
3277	     * particular entry, plus three widths:  the width of the
3278	     * label, the width to allow for an indicator to be displayed
3279	     * to the left of the label (if any), and the width of the
3280	     * accelerator to be displayed to the right of the label
3281	     * (if any). These sizes depend, of course, on the type
3282	     * of the entry.
3283	     */
3284
3285	    GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &labelWidth, &height);
3286	    mePtr->height = height;
3287
3288	    nonAccelMargin = 0;
3289	    if (mePtr->type == CASCADE_ENTRY) {
3290		GetMenuAccelGeometry(menuPtr, mePtr, tkfont, fmPtr,
3291			&modifierWidth, &accelWidth, &height);
3292	    } else if (mePtr->accelLength == 0) {
3293		if (haveAccel && !mePtr->hideMargin) {
3294		    if (IS_THEME_MENU_FONT(tkfont)) {
3295			nonAccelMargin = menuSymbols[COMMAND_SYMBOL].width;
3296		    } else {
3297			nonAccelMargin = Tk_TextWidth(tkfont,
3298				menuSymbols[COMMAND_SYMBOL].utf,
3299				menuSymbols[COMMAND_SYMBOL].utfLen);
3300		    }
3301		}
3302		accelWidth = modifierWidth = 0;
3303	    } else {
3304		GetMenuAccelGeometry(menuPtr, mePtr, tkfont,
3305			fmPtr, &modifierWidth, &accelWidth, &height);
3306		if (height > mePtr->height) {
3307		    mePtr->height = height;
3308		}
3309	    }
3310
3311	    if (!(mePtr->hideMargin)) {
3312		GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont,
3313			fmPtr, &indicatorSpace, &height);
3314		if (height > mePtr->height) {
3315		    mePtr->height = height;
3316		}
3317	    } else {
3318		indicatorSpace = 0;
3319	    }
3320
3321	    if (nonAccelMargin > maxNonAccelMargin) {
3322		maxNonAccelMargin = nonAccelMargin;
3323	    }
3324	    if (accelWidth > maxAccelTextWidth) {
3325		maxAccelTextWidth = accelWidth;
3326	    }
3327	    if (modifierWidth > maxModifierWidth) {
3328		maxModifierWidth = modifierWidth;
3329	    }
3330	    if (indicatorSpace > maxIndicatorSpace) {
3331		maxIndicatorSpace = indicatorSpace;
3332	    }
3333
3334	    entryWidth = labelWidth + modifierWidth + accelWidth
3335		    + nonAccelMargin;
3336
3337	    if (entryWidth > maxWidth) {
3338		maxWidth = entryWidth;
3339	    }
3340
3341	    if (mePtr->accelLength > 0) {
3342		if (entryWidth > maxEntryWithAccelWidth) {
3343		    maxEntryWithAccelWidth = entryWidth;
3344		}
3345	    } else {
3346		if (entryWidth > maxEntryWithoutAccelWidth) {
3347		    maxEntryWithoutAccelWidth = entryWidth;
3348		}
3349	    }
3350	    mePtr->height += 2 * activeBorderWidth;
3351	}
3352	mePtr->y = y;
3353	y += menuPtr->entries[i]->height + borderWidth;
3354	if (y > windowHeight) {
3355	    windowHeight = y;
3356	}
3357    }
3358
3359    for (j = lastColumnBreak; j < menuPtr->numEntries; j++) {
3360	columnEntryPtr = menuPtr->entries[j];
3361	geometryPtr = (EntryGeometry *) columnEntryPtr->platformEntryData;
3362
3363	columnEntryPtr->indicatorSpace = maxIndicatorSpace;
3364	columnEntryPtr->width = maxIndicatorSpace + maxWidth
3365		+ 2 * activeBorderWidth;
3366	geometryPtr->accelTextWidth = maxAccelTextWidth;
3367	columnEntryPtr->x = x;
3368	columnEntryPtr->entryFlags |= ENTRY_LAST_COLUMN;
3369	if (maxEntryWithoutAccelWidth > maxEntryWithAccelWidth) {
3370	    geometryPtr->nonAccelMargin = maxEntryWithoutAccelWidth
3371		    - maxEntryWithAccelWidth;
3372	    if (geometryPtr->nonAccelMargin > maxNonAccelMargin) {
3373		geometryPtr->nonAccelMargin = maxNonAccelMargin;
3374	    }
3375	} else {
3376	    geometryPtr->nonAccelMargin = 0;
3377	}
3378    }
3379    windowWidth = x + maxIndicatorSpace + maxWidth
3380	    + 2 * activeBorderWidth + borderWidth;
3381    windowHeight += borderWidth;
3382
3383    /*
3384     * The X server doesn't like zero dimensions, so round up to at least
3385     * 1 (a zero-sized menu should never really occur, anyway).
3386     */
3387
3388    if (windowWidth <= 0) {
3389	windowWidth = 1;
3390    }
3391    if (windowHeight <= 0) {
3392	windowHeight = 1;
3393    }
3394    menuPtr->totalWidth = windowWidth;
3395    menuPtr->totalHeight = windowHeight;
3396}
3397
3398/*
3399 *----------------------------------------------------------------------
3400 *
3401 * DrawMenuEntryLabel --
3402 *
3403 *	This procedure draws the label part of a menu.
3404 *
3405 * Results:
3406 *	None.
3407 *
3408 * Side effects:
3409 *	Commands are output to X to display the menu in its
3410 *	current mode.
3411 *
3412 *----------------------------------------------------------------------
3413 */
3414
3415void
3416DrawMenuEntryLabel(
3417    TkMenu *menuPtr,		/* The menu we are drawing */
3418    TkMenuEntry *mePtr,		/* The entry we are drawing */
3419    Drawable d,			/* What we are drawing into */
3420    GC gc,			/* The gc we are drawing into */
3421    Tk_Font tkfont,		/* The precalculated font */
3422    const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */
3423    int x,			/* left edge */
3424    int y,			/* right edge */
3425    int width,			/* width of entry */
3426    int height)			/* height of entry */
3427{
3428    int imageWidth, imageHeight, textWidth = 0, textHeight = 0;
3429    int indicatorSpace =  mePtr->indicatorSpace;
3430    int leftEdge = x + indicatorSpace;
3431    int haveImage = 0, haveText = 0;
3432    int imageXOffset = 0, imageYOffset = 0;
3433    int textXOffset = 0, textYOffset = 0;
3434    Pixmap bitmap = (Pixmap) NULL;
3435    Tcl_DString itemTextDString;
3436
3437    /*
3438     * Work out what we will need to draw first.
3439     */
3440
3441    if (mePtr->image != NULL) {
3442	Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight);
3443	haveImage = 1;
3444    } else if (mePtr->bitmapPtr != NULL) {
3445	bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
3446	Tk_SizeOfBitmap(menuPtr->display, bitmap, &imageWidth, &imageHeight);
3447	haveImage = 1;
3448    }
3449    if (!haveImage || (mePtr->compound != COMPOUND_NONE)) {
3450	if (mePtr->labelLength > 0) {
3451	    GetEntryText(mePtr, &itemTextDString);
3452	    if (mePtr->compound != COMPOUND_NONE) {
3453		textWidth = Tk_TextWidth(tkfont,
3454			Tcl_DStringValue(&itemTextDString),
3455			Tcl_DStringLength(&itemTextDString)) +
3456			menuTextLeadingEdgeMargin + menuTextTrailingEdgeMargin;
3457		textHeight = fmPtr->linespace;
3458	    }
3459	    haveText = 1;
3460	}
3461    }
3462
3463    /*
3464     * Now work out what the relative positions are.
3465     */
3466
3467    if (haveImage && haveText && (mePtr->compound != COMPOUND_NONE)) {
3468	int fullWidth = (imageWidth > textWidth ? imageWidth : textWidth);
3469
3470	switch ((enum compound) mePtr->compound) {
3471	    case COMPOUND_TOP:
3472		textXOffset = (fullWidth - textWidth)/2;
3473		textYOffset = imageHeight/2 + 2;
3474		imageXOffset = (fullWidth - imageWidth)/2;
3475		imageYOffset = -textHeight/2;
3476		break;
3477	    case COMPOUND_BOTTOM:
3478		textXOffset = (fullWidth - textWidth)/2;
3479		textYOffset = -imageHeight/2;
3480		imageXOffset = (fullWidth - imageWidth)/2;
3481		imageYOffset = textHeight/2 + 2;
3482		break;
3483	    case COMPOUND_LEFT:
3484		/*
3485		 * Position image in the indicator space to the left of the
3486		 * entries, unless this entry is a radio|check button because
3487		 * then the indicator space will be used.
3488		 */
3489
3490		textXOffset = imageWidth + 2 - menuTextLeadingEdgeMargin;
3491		if ((mePtr->type != CHECK_BUTTON_ENTRY)
3492			&& (mePtr->type != RADIO_BUTTON_ENTRY)) {
3493		    textXOffset -= indicatorSpace;
3494		    imageXOffset = -indicatorSpace;
3495		}
3496		if (textXOffset < 0) {
3497		    textXOffset = 0;
3498		}
3499		break;
3500	    case COMPOUND_RIGHT:
3501		imageXOffset = textWidth + 2 - menuTextTrailingEdgeMargin;
3502		break;
3503	    case COMPOUND_CENTER:
3504		textXOffset = (fullWidth - textWidth)/2;
3505		imageXOffset = (fullWidth - imageWidth)/2;
3506		break;
3507	    case COMPOUND_NONE:
3508	    	/*
3509	    	 * Never reached.
3510	    	 */
3511		break;
3512	}
3513    }
3514
3515    /*
3516     * Draw label and/or bitmap or image for entry.
3517     */
3518
3519    if (mePtr->image != NULL) {
3520	if ((mePtr->selectImage != NULL)
3521		&& (mePtr->entryFlags & ENTRY_SELECTED)) {
3522	    Tk_RedrawImage(mePtr->selectImage, 0, 0, imageWidth, imageHeight,
3523		    d, leftEdge + imageXOffset,
3524		    y + (mePtr->height - imageHeight)/2 + imageYOffset);
3525	} else {
3526	    Tk_RedrawImage(mePtr->image, 0, 0, imageWidth, imageHeight,
3527		    d, leftEdge + imageXOffset,
3528		    y + (mePtr->height - imageHeight)/2 + imageYOffset);
3529	}
3530    } else if (mePtr->bitmapPtr != NULL) {
3531	XCopyPlane(menuPtr->display, bitmap, d, gc, 0, 0, imageWidth,
3532		imageHeight, leftEdge + imageXOffset,
3533		y + (mePtr->height - imageHeight)/2  + imageYOffset, 1);
3534    }
3535    if (haveText) {
3536	int baseline = y + (height + fmPtr->ascent - fmPtr->descent)/2;
3537
3538	Tk_DrawChars(menuPtr->display, d, gc, tkfont,
3539		Tcl_DStringValue(&itemTextDString),
3540		Tcl_DStringLength(&itemTextDString),
3541		leftEdge + menuTextLeadingEdgeMargin + textXOffset,
3542		baseline + textYOffset);
3543	Tcl_DStringFree(&itemTextDString);
3544    }
3545
3546    if (mePtr->state == ENTRY_DISABLED) {
3547	if (menuPtr->disabledFgPtr == NULL) {
3548	    /* XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y,
3549		    width, height); */
3550	} else if ((mePtr->image != NULL)
3551		&& (menuPtr->disabledImageGC != None)) {
3552	    XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
3553		    leftEdge + imageXOffset,
3554		    y + (mePtr->height - imageHeight)/2 + imageYOffset,
3555		    imageWidth, imageHeight);
3556	}
3557    }
3558}
3559
3560/*
3561 *----------------------------------------------------------------------
3562 *
3563 * DrawMenuEntryBackground --
3564 *
3565 *	This procedure draws the background part of a menu entry.
3566 *	Under Appearance, we only draw the background if the entry's
3567 *	border is set, we DO NOT inherit it from the menu...
3568 *
3569 * Results:
3570 *	None.
3571 *
3572 * Side effects:
3573 *	Commands are output to X to display the menu in its
3574 *	current mode.
3575 *
3576 *----------------------------------------------------------------------
3577 */
3578
3579void
3580DrawMenuEntryBackground(
3581    TkMenu *menuPtr,		/* The menu we are drawing. */
3582    TkMenuEntry *mePtr,		/* The entry we are drawing. */
3583    Drawable d,			/* What we are drawing into */
3584    Tk_3DBorder activeBorder,	/* Border for active items */
3585    Tk_3DBorder bgBorder,	/* Border for the background */
3586    int x,			/* left edge */
3587    int y,			/* top edge */
3588    int width,			/* width of rectangle to draw */
3589    int height)			/* height of rectangle to draw */
3590{
3591    if ((menuPtr->menuType == TEAROFF_MENU)
3592	    || ((mePtr->state == ENTRY_ACTIVE)
3593		    && (mePtr->activeBorderPtr != None))
3594	    || ((mePtr->state != ENTRY_ACTIVE) && (mePtr->borderPtr != None))) {
3595	if (mePtr->state == ENTRY_ACTIVE) {
3596	    bgBorder = activeBorder;
3597	}
3598	Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder,
3599		x, y, width, height, 0, TK_RELIEF_FLAT);
3600    }
3601}
3602
3603/*
3604 *----------------------------------------------------------------------
3605 *
3606 * GetMenuLabelGeometry --
3607 *
3608 *	Figures out the size of the label portion of a menu item.
3609 *
3610 * Results:
3611 *	widthPtr and heightPtr are filled in with the correct geometry
3612 *	information.
3613 *
3614 * Side effects:
3615 *	None.
3616 *
3617 *----------------------------------------------------------------------
3618 */
3619
3620void
3621GetMenuLabelGeometry(
3622    TkMenuEntry *mePtr,		/* The entry we are computing */
3623    Tk_Font tkfont,		/* The precalculated font */
3624    const Tk_FontMetrics *fmPtr,/* The precalculated metrics */
3625    int *widthPtr,		/* The resulting width of the label portion */
3626    int *heightPtr)		/* The resulting height of the label portion */
3627{
3628    TkMenu *menuPtr = mePtr->menuPtr;
3629    int haveImage = 0, tornOff = (menuPtr->menuType == TEAROFF_MENU);
3630#ifdef USE_TK_MDEF
3631    const int useMDEF = ((MacMenu *) menuPtr->platformData)->useMDEF;
3632#endif
3633
3634    if (mePtr->image != NULL && (useMDEF || tornOff)) {
3635	Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr);
3636	haveImage = 1;
3637    } else if (mePtr->bitmapPtr != NULL && (useMDEF || tornOff)) {
3638	Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
3639	Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr);
3640	haveImage = 1;
3641    }
3642    if (!haveImage || (mePtr->compound != COMPOUND_NONE)) {
3643	int textWidth = 0, textHeight = fmPtr->linespace;
3644
3645	if (mePtr->labelPtr != NULL) {
3646	    Tcl_DString itemTextDString;
3647
3648	    GetEntryText(mePtr, &itemTextDString);
3649	    textWidth = Tk_TextWidth(tkfont,
3650		    Tcl_DStringValue(&itemTextDString),
3651		    Tcl_DStringLength(&itemTextDString)) +
3652		    menuTextLeadingEdgeMargin + menuTextTrailingEdgeMargin;
3653	    Tcl_DStringFree(&itemTextDString);
3654
3655	    if (haveImage && (mePtr->compound != COMPOUND_NONE)) {
3656		switch ((enum compound) mePtr->compound) {
3657		    int margin;
3658
3659		    case COMPOUND_TOP:
3660		    case COMPOUND_BOTTOM:
3661			if (textWidth > *widthPtr) {
3662			    *widthPtr = textWidth;
3663			}
3664			*heightPtr += textHeight + 2;
3665			break;
3666		    case COMPOUND_LEFT:
3667			margin = *widthPtr + 2;
3668			if (margin > menuTextLeadingEdgeMargin) {
3669			    margin = menuTextLeadingEdgeMargin;
3670			}
3671			*widthPtr += textWidth + 2 - margin;
3672			if (textHeight > *heightPtr) {
3673			    *heightPtr = textHeight;
3674			}
3675			break;
3676		    case COMPOUND_RIGHT:
3677			margin = menuTextTrailingEdgeMargin;
3678			*widthPtr += textWidth + 2 - margin;
3679			if (textHeight > *heightPtr) {
3680			    *heightPtr = textHeight;
3681			}
3682			break;
3683		    case COMPOUND_CENTER:
3684			if (textWidth > *widthPtr) {
3685			    *widthPtr = textWidth;
3686			}
3687			if (textHeight > *heightPtr) {
3688			    *heightPtr = textHeight;
3689			}
3690			break;
3691		    case COMPOUND_NONE:
3692			/*
3693			 * Never reached.
3694			 */
3695			break;
3696		}
3697		goto labelGeomDone;
3698	    }
3699	}
3700	*widthPtr = textWidth;
3701	*heightPtr = textHeight;
3702    }
3703
3704labelGeomDone:
3705    *heightPtr += menuItemExtraHeight;
3706    *widthPtr += menuItemExtraWidth;
3707}
3708
3709/*
3710 *----------------------------------------------------------------------
3711 *
3712 * TkMacOSXGenerateParentMenuSelectEvent --
3713 *
3714 *	Respond to a hierarchical menu being opened.
3715 *
3716 * Results:
3717 *	True if event(s) are generated - false otherwise.
3718 *
3719 * Side effects:
3720 *	Places a virtual event on the event queue.
3721 *
3722 *----------------------------------------------------------------------
3723 */
3724
3725int
3726TkMacOSXGenerateParentMenuSelectEvent(
3727    MenuRef menu)
3728{
3729    TkMenu *menuPtr = MenuPtrForMenuRef(menu);
3730
3731    if (menuPtr) {
3732	TkMenuEntry *parentEntryPtr = GetParentMenuEntry(menuPtr);
3733
3734	if (parentEntryPtr && (menuPtr = parentEntryPtr->menuPtr)) {
3735	    TkActivateMenuEntry(menuPtr, parentEntryPtr->index);
3736	    MenuSelectEvent(menuPtr);
3737	    Tcl_ServiceAll();
3738	    return true;
3739	}
3740    }
3741    return false;
3742}
3743
3744/*
3745 *----------------------------------------------------------------------
3746 *
3747 * TkMacOSXGenerateMenuSelectEvent --
3748 *
3749 *	Respond to a menu item being selected.
3750 *
3751 * Results:
3752 *	True if event(s) are generated - false otherwise.
3753 *
3754 * Side effects:
3755 *	Places a virtual event on the event queue.
3756 *
3757 *----------------------------------------------------------------------
3758 */
3759
3760int
3761TkMacOSXGenerateMenuSelectEvent(
3762    MenuRef menu,
3763    MenuItemIndex index)
3764{
3765    TkMenu *menuPtr = MenuPtrForMenuRef(menu);
3766    int item = index - 1;
3767
3768    if (menuPtr) {
3769	if (item < 0 || item >= menuPtr->numEntries ||
3770		(menuPtr->entries[item])->state == ENTRY_DISABLED) {
3771	    TkActivateMenuEntry(menuPtr, -1);
3772	} else {
3773	    TkActivateMenuEntry(menuPtr, item);
3774	    MenuSelectEvent(menuPtr);
3775	    Tcl_ServiceAll();
3776	    return true;
3777	}
3778    }
3779    return false;
3780}
3781
3782/*
3783 *----------------------------------------------------------------------
3784 *
3785 * MenuSelectEvent --
3786 *
3787 *	Generates a "MenuSelect" virtual event. This can be used to
3788 *	do context-sensitive menu help.
3789 *
3790 * Results:
3791 *	None.
3792 *
3793 * Side effects:
3794 *	Places a virtual event on the event queue.
3795 *
3796 *----------------------------------------------------------------------
3797 */
3798
3799void
3800MenuSelectEvent(
3801    TkMenu *menuPtr)		/* the menu we have selected. */
3802{
3803    XVirtualEvent event;
3804
3805    bzero(&event, sizeof(XVirtualEvent));
3806    event.type = VirtualEvent;
3807    event.serial = menuPtr->display->request;
3808    event.send_event = false;
3809    event.display = menuPtr->display;
3810    Tk_MakeWindowExist(menuPtr->tkwin);
3811    event.event = Tk_WindowId(menuPtr->tkwin);
3812    event.root = XRootWindow(menuPtr->display, 0);
3813    event.subwindow = None;
3814    event.time = TkpGetMS();
3815
3816    XQueryPointer(NULL, None, NULL, NULL, &event.x_root, &event.y_root, NULL,
3817	    NULL, &event.state);
3818    event.same_screen = true;
3819    event.name = Tk_GetUid("MenuSelect");
3820    Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL);
3821}
3822
3823/*
3824 *----------------------------------------------------------------------
3825 *
3826 * TkMacOSXClearActiveMenu --
3827 *
3828 *	Clears Tk's active entry for the given MenuRef.
3829 *
3830 * Results:
3831 *	None.
3832 *
3833 * Side effects:
3834 *	Generates <<MenuSelect>> virtual events.
3835 *
3836 *----------------------------------------------------------------------
3837 */
3838
3839void
3840TkMacOSXClearActiveMenu(
3841    MenuRef menu)
3842{
3843    TkMenu *menuPtr = MenuPtrForMenuRef(menu);
3844
3845    if (menuPtr) {
3846	RecursivelyClearActiveMenu(menuPtr);
3847    }
3848}
3849
3850/*
3851 *----------------------------------------------------------------------
3852 *
3853 * RecursivelyClearActiveMenu --
3854 *
3855 *	Recursively clears the active entry in the menu's cascade hierarchy.
3856 *
3857 * Results:
3858 *	None.
3859 *
3860 * Side effects:
3861 *	Generates <<MenuSelect>> virtual events.
3862 *
3863 *----------------------------------------------------------------------
3864 */
3865
3866void
3867RecursivelyClearActiveMenu(
3868    TkMenu *menuPtr)		/* The menu to reset. */
3869{
3870    int i;
3871    TkMenuEntry *mePtr;
3872
3873    TkActivateMenuEntry(menuPtr, -1);
3874    for (i = 0; i < menuPtr->numEntries; i++) {
3875	mePtr = menuPtr->entries[i];
3876	if (mePtr->type == CASCADE_ENTRY) {
3877	    if ((mePtr->childMenuRefPtr != NULL)
3878		    && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
3879		RecursivelyClearActiveMenu(mePtr->childMenuRefPtr->menuPtr);
3880	    }
3881	}
3882    }
3883}
3884
3885/*
3886 *----------------------------------------------------------------------
3887 *
3888 * TkMacOSXClearMenubarActive --
3889 *
3890 *	Recursively clears the active entry in the current menubar hierarchy.
3891 *
3892 * Results:
3893 *	None.
3894 *
3895 * Side effects:
3896 *	Generates <<MenuSelect>> virtual events.
3897 *
3898 *----------------------------------------------------------------------
3899 */
3900
3901void
3902TkMacOSXClearMenubarActive(void)
3903{
3904    TkMenuReferences *menuBarRefPtr;
3905
3906    if (currentMenuBarName != NULL) {
3907	menuBarRefPtr = TkFindMenuReferences(currentMenuBarInterp,
3908		currentMenuBarName);
3909	if ((menuBarRefPtr != NULL) && (menuBarRefPtr->menuPtr != NULL)) {
3910	    TkMenu *menuPtr;
3911
3912	    for (menuPtr = menuBarRefPtr->menuPtr->masterMenuPtr;
3913		    menuPtr != NULL; menuPtr = menuPtr->nextInstancePtr) {
3914		if (menuPtr->menuType == MENUBAR) {
3915		    RecursivelyClearActiveMenu(menuPtr);
3916		}
3917	    }
3918	}
3919    }
3920}
3921
3922/*
3923 *----------------------------------------------------------------------
3924 *
3925 * TkpMenuNotifyToplevelCreate --
3926 *
3927 *	This routine reconfigures the menu and the clones indicated by
3928 *	menuName becuase a toplevel has been created and any system
3929 *	menus need to be created. Only applicable to Windows.
3930 *
3931 * Results:
3932 *	None.
3933 *
3934 * Side effects:
3935 *	An idle handler is set up to do the reconfiguration.
3936 *
3937 *----------------------------------------------------------------------
3938 */
3939
3940void
3941TkpMenuNotifyToplevelCreate(
3942    Tcl_Interp *interp,		/* The interp the menu lives in. */
3943    char *menuName)		/* The name of the menu to reconfigure. */
3944{
3945    /*
3946     * Nothing to do.
3947     */
3948}
3949
3950/*
3951 *----------------------------------------------------------------------
3952 *
3953 * TkpMenuInit --
3954 *
3955 *	Initializes Mac-specific menu data.
3956 *
3957 * Results:
3958 *	None.
3959 *
3960 * Side effects:
3961 *	Allocates a hash table.
3962 *
3963 *----------------------------------------------------------------------
3964 */
3965
3966void
3967TkpMenuInit(void)
3968{
3969    MenuSymbol *ms = menuSymbols;
3970    CFStringRef cfStr;
3971
3972    lastMenuID = 256;
3973    Tcl_InitHashTable(&commandTable, TCL_ONE_WORD_KEYS);
3974    currentMenuBarOwner = NULL;
3975    currentAppleMenuID = 0;
3976    currentHelpMenuID = 0;
3977    currentMenuBarInterp = NULL;
3978    currentMenuBarName = NULL;
3979    windowListPtr = NULL;
3980
3981#ifdef USE_TK_MDEF
3982    tkThemeMenuItemDrawingUPP
3983	    = NewMenuItemDrawingUPP(ThemeMenuItemDrawingProc);
3984    useMDEFVar = Tcl_NewStringObj("::tk::mac::useCustomMDEF", -1);
3985    macMDEFDrawable.winPtr = NULL;
3986    macMDEFDrawable.xOff = 0;
3987    macMDEFDrawable.yOff = 0;
3988    macMDEFDrawable.visRgn = NULL;
3989    macMDEFDrawable.aboveVisRgn = NULL;
3990    macMDEFDrawable.drawRect = CGRectNull;
3991    macMDEFDrawable.referenceCount = 0;
3992    macMDEFDrawable.toplevel = NULL;
3993    macMDEFDrawable.flags = 0;
3994    macMDEFDrawable.grafPtr = NULL;
3995    macMDEFDrawable.context = NULL;
3996    macMDEFDrawable.size = CGSizeZero;
3997#endif
3998
3999    ChkErr(GetThemeMetric, kThemeMetricMenuMarkColumnWidth,
4000	    &menuMarkColumnWidth);
4001    ChkErr(GetThemeMetric, kThemeMetricMenuMarkIndent, &menuMarkIndent);
4002    ChkErr(GetThemeMetric, kThemeMetricMenuTextLeadingEdgeMargin,
4003	    &menuTextLeadingEdgeMargin);
4004    ChkErr(GetThemeMetric, kThemeMetricMenuTextTrailingEdgeMargin,
4005	    &menuTextTrailingEdgeMargin);
4006    ChkErr(GetThemeMenuItemExtra, kThemeMenuItemPlain, &menuItemExtraHeight,
4007	    &menuItemExtraWidth);
4008    ChkErr(GetThemeMenuSeparatorHeight, &menuSeparatorHeight);
4009
4010    while (ms->unicode) {
4011	ms->utfLen = Tcl_UniCharToUtf(ms->unicode, ms->utf);
4012	ms->utf[ms->utfLen] = 0;
4013	cfStr = CFStringCreateWithCharacters(NULL, &ms->unicode, 1);
4014	if (cfStr) {
4015	    ms->width = MeasureThemeText(cfStr, kThemeMenuItemCmdKeyFont);
4016	    CFRelease(cfStr);
4017	}
4018	ms++;
4019    }
4020}
4021
4022/*
4023 *----------------------------------------------------------------------
4024 *
4025 * TkpMenuThreadInit --
4026 *
4027 *	Does platform-specific initialization of thread-specific
4028 *	menu state.
4029 *
4030 * Results:
4031 *	None.
4032 *
4033 * Side effects:
4034 *	None.
4035 *
4036 *----------------------------------------------------------------------
4037 */
4038
4039void
4040TkpMenuThreadInit(void)
4041{
4042    /*
4043     * Nothing to do.
4044     */
4045}
4046
4047/*
4048 *----------------------------------------------------------------------
4049 *
4050 * TkpPreprocessMacMenu --
4051 *
4052 *    Handle preprocessing of menubar if it exists.
4053 *
4054 * Results:
4055 *    None.
4056 *
4057 * Side effects:
4058 *    All post commands for the current menubar get executed.
4059 *
4060 *----------------------------------------------------------------------
4061 */
4062
4063void
4064TkMacOSXPreprocessMenu(void)
4065{
4066    if ((currentMenuBarName != NULL) && (currentMenuBarInterp != NULL)) {
4067	TkMenuReferences *mbRefPtr =
4068		TkFindMenuReferences(currentMenuBarInterp,currentMenuBarName);
4069
4070	if ((mbRefPtr != NULL) && (mbRefPtr->menuPtr != NULL)) {
4071	    int code;
4072
4073	    Tcl_Preserve((ClientData) currentMenuBarInterp);
4074	    code = TkPreprocessMenu(mbRefPtr->menuPtr->masterMenuPtr);
4075	    if ((code != TCL_OK) && (code != TCL_CONTINUE)
4076		    && (code != TCL_BREAK)) {
4077		Tcl_AddErrorInfo(currentMenuBarInterp,
4078			"\n    (menu preprocess)");
4079		Tcl_BackgroundError(currentMenuBarInterp);
4080	    }
4081	    Tcl_Release((ClientData) currentMenuBarInterp);
4082	}
4083    }
4084}
4085
4086#ifdef USE_TK_MDEF
4087#pragma mark MDEF
4088/*
4089 *----------------------------------------------------------------------
4090 *
4091 * MenuDefProc --
4092 *
4093 *	This routine is the MDEF handler for Tk. It receives all messages
4094 *	for the menu and dispatches them.
4095 *
4096 * Results:
4097 *	None.
4098 *
4099 * Side effects:
4100 *	This routine causes menus to be drawn and will certainly allocate
4101 *	memory as a result. Also, the menu can scroll up and down, and
4102 *	various other interface actions can take place.
4103 *
4104 *----------------------------------------------------------------------
4105 */
4106
4107void
4108MenuDefProc(
4109    SInt16 message,		/* What action are we taking? */
4110    MenuRef menu,		/* The menu we are working with */
4111    Rect *menuRectPtr,		/* A pointer to the rect for the
4112				 * whole menu. */
4113    Point hitPt,		/* Where the mouse was clicked for
4114				 * the appropriate messages. */
4115    SInt16 *whichItem)		/* Output result. Which item was
4116				 * hit by the user? */
4117{
4118    TkMenu *menuPtr;
4119    Tcl_HashEntry *commandEntryPtr;
4120    MenuID menuID;
4121
4122    menuID = GetMenuID(menu);
4123    commandEntryPtr = Tcl_FindHashEntry(&commandTable, (char*)(intptr_t)menuID);
4124
4125    if (!commandEntryPtr) return;
4126    menuPtr = (TkMenu *) Tcl_GetHashValue(commandEntryPtr);
4127
4128    switch (message) {
4129	case kMenuInitMsg:
4130	    *whichItem = noErr;
4131	    break;
4132	case kMenuDisposeMsg:
4133	    break;
4134	case kMenuHiliteItemMsg:
4135	    HandleMenuHiliteMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr);
4136	    break;
4137	case kMenuCalcItemMsg:
4138	    HandleMenuCalcItemMsg(menu, menuRectPtr, hitPt, whichItem,
4139		    menuPtr);
4140	    break;
4141	case kMenuDrawItemsMsg:
4142#ifdef TK_MAC_DEBUG_MENUS
4143	    TkMacOSXDbgMsg("MDEF: DrawItemsMsg");
4144#endif
4145	    /*
4146	     * We do nothing  here, because we don't support the Menu Managers
4147	     * dynamic item groups
4148	     */
4149	    break;
4150	case kMenuThemeSavvyMsg:
4151	    *whichItem = kThemeSavvyMenuResponse;
4152	    break;
4153	case kMenuSizeMsg:
4154#ifdef TK_MAC_DEBUG_MENUS
4155	    TkMacOSXDbgMsg("MDEF: SizeMsg %d, %d", hitPt.h, hitPt.v);
4156#endif
4157	    SetMenuWidth(menu, hitPt.h < menuPtr->totalWidth ?	hitPt.h :
4158		    menuPtr->totalWidth);
4159	    SetMenuHeight(menu, hitPt.v < menuPtr->totalHeight ? hitPt.v :
4160		    menuPtr->totalHeight);
4161	    break;
4162	case kMenuDrawMsg:
4163	    HandleMenuDrawMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr);
4164	    break;
4165	case kMenuFindItemMsg:
4166	    HandleMenuFindItemMsg(menu, menuRectPtr, hitPt, whichItem,
4167		    menuPtr);
4168	    break;
4169	case kMenuPopUpMsg:
4170	    HandleMenuPopUpMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr);
4171	    break;
4172    }
4173}
4174
4175/*
4176 *----------------------------------------------------------------------
4177 *
4178 * HandleMenuHiliteMsg --
4179 *
4180 *	Handles the MenuDefProc's hilite message.
4181 *
4182 * Results:
4183 *	A menu entry is drawn
4184 *
4185 * Side effects:
4186 *	None
4187 *
4188 *----------------------------------------------------------------------
4189 */
4190
4191void
4192HandleMenuHiliteMsg(
4193    MenuRef menu,
4194    Rect *menuRectPtr,
4195    Point hitPt,
4196    SInt16 *whichItem,
4197    TkMenu *menuPtr)
4198{
4199    OSStatus err;
4200    Tk_Font tkfont;
4201    Tk_FontMetrics fontMetrics;
4202    MDEFHiliteItemData *hidPtr = (MDEFHiliteItemData *)whichItem;
4203    int oldItem = hidPtr->previousItem - 1;
4204    int newItem = hidPtr->newItem - 1;
4205    MenuTrackingData mtd, *mtdPtr = &mtd;
4206
4207#ifdef TK_MAC_DEBUG_MENUS
4208    TkMacOSXDbgMsg("MDEF: HiliteMsg %d -> %d", hidPtr->previousItem,
4209	    hidPtr->newItem);
4210#endif
4211    GetPort(&macMDEFDrawable.grafPtr);
4212    macMDEFDrawable.context = (CGContextRef) hidPtr->context;
4213
4214    err = ChkErr(GetMenuTrackingData, menu, mtdPtr);
4215    if (err != noErr) {
4216	return;
4217    }
4218
4219    tkfont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
4220    Tk_GetFontMetrics(tkfont, &fontMetrics);
4221    if (oldItem >= 0) {
4222	AppearanceEntryDrawWrapper(menuPtr->entries[oldItem], menuRectPtr,
4223		mtdPtr, (Drawable) &macMDEFDrawable, &fontMetrics, tkfont, 1);
4224    }
4225    if (newItem >= 0) {
4226	AppearanceEntryDrawWrapper(menuPtr->entries[newItem], menuRectPtr,
4227		mtdPtr, (Drawable) &macMDEFDrawable, &fontMetrics, tkfont, 0);
4228    }
4229}
4230
4231/*
4232 *----------------------------------------------------------------------
4233 *
4234 * HandleMenuDrawMsg --
4235 *
4236 *	Handles the MenuDefProc's draw message.
4237 *
4238 * Results:
4239 *	A menu entry is drawn
4240 *
4241 * Side effects:
4242 *	None
4243 *
4244 *----------------------------------------------------------------------
4245 */
4246
4247void
4248HandleMenuDrawMsg(
4249    MenuRef menu,
4250    Rect *menuRectPtr,
4251    Point hitPt,
4252    SInt16 *whichItem,
4253    TkMenu *menuPtr)
4254{
4255    Tk_Font menuFont;
4256    Tk_FontMetrics fontMetrics;
4257    TkMenuEntry *mePtr;
4258    int i;
4259    Rect menuClipRect, bounds;
4260    MDEFDrawData *ddPtr = (MDEFDrawData*)whichItem;
4261    MenuTrackingData *mtdPtr = &(ddPtr->trackingData);
4262    TkWindow *winPtr = (TkWindow*)menuPtr->tkwin;
4263
4264    GetPort(&macMDEFDrawable.grafPtr);
4265    GetPortBounds(macMDEFDrawable.grafPtr, &bounds);
4266    macMDEFDrawable.context = (CGContextRef) ddPtr->context;
4267#ifdef TK_MAC_DEBUG_MENUS
4268    TkMacOSXDbgMsg("MDEF: DrawMsg %d - %d; %d - %d", menuRectPtr->top,
4269	    menuRectPtr->bottom, bounds.top, bounds.bottom);
4270#endif
4271    winPtr->changes.x = menuRectPtr->left;
4272    winPtr->changes.y = menuRectPtr->top;
4273    winPtr->changes.width = menuRectPtr->right - menuRectPtr->left;
4274    winPtr->changes.height = menuRectPtr->bottom - menuRectPtr->top;
4275    TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable,
4276	    0, 0, -1, -1);
4277#if 0
4278    if (menuPtr->menuRefPtr->topLevelListPtr != NULL) {
4279	menuType = kThemeMenuTypePullDown;
4280    } else if (menuPtr->menuRefPtr->parentEntryPtr != NULL) {
4281	menuType = kThemeMenuTypeHierarchical;
4282    } else {
4283	menuType = kThemeMenuTypePopUp;
4284    }
4285#endif
4286    DrawMenuBackground(menuPtr, menuRectPtr, (Drawable) &macMDEFDrawable);
4287    menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
4288    Tk_GetFontMetrics(menuFont, &fontMetrics);
4289    menuClipRect = *menuRectPtr;
4290    mtdPtr->virtualMenuBottom = mtdPtr->virtualMenuTop + menuPtr->totalHeight;
4291
4292    /*
4293     * Next, figure out scrolling information.
4294     */
4295
4296    if ((menuRectPtr->bottom - menuRectPtr->top) < menuPtr->totalHeight) {
4297	short arrowHeight = fontMetrics.linespace + 1;
4298	Rect arrowRect, eraseRect;
4299	ThemeMenuState menuState = IsMenuItemEnabled(menu, 0) ?
4300		kThemeMenuActive : kThemeMenuDisabled;
4301
4302	if (mtdPtr->virtualMenuTop < menuRectPtr->top) {
4303	    arrowRect = bounds;
4304	    /*arrowRect.top += 1;*/
4305	    arrowRect.bottom = arrowRect.top + arrowHeight;
4306	    eraseRect = arrowRect;
4307	    eraseRect.top = menuRectPtr->top;
4308	    menuClipRect.top = arrowRect.bottom;
4309	    ChkErr(EraseMenuBackground, menu, &eraseRect,
4310		    macMDEFDrawable.context);
4311	    ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect,
4312		    mtdPtr->virtualMenuTop, mtdPtr->virtualMenuBottom,
4313		    menuState, kThemeMenuItemScrollUpArrow, NULL, 0);
4314#ifdef TK_MAC_DEBUG_MENUS
4315	    TkMacOSXDbgMsg("upArrow:   %d - %d, %d - %d", arrowRect.top,
4316		    arrowRect.bottom, arrowRect.left, arrowRect.right);
4317#endif
4318	}
4319	if (mtdPtr->virtualMenuBottom > menuRectPtr->bottom) {
4320	    arrowRect = bounds;
4321	    arrowRect.bottom -= 1;
4322	    arrowRect.top = arrowRect.bottom - arrowHeight;
4323	    eraseRect = arrowRect;
4324	    eraseRect.bottom = menuRectPtr->bottom;
4325	    menuClipRect.bottom = arrowRect.top;
4326	    ChkErr(EraseMenuBackground, menu, &eraseRect,
4327		    macMDEFDrawable.context);
4328	    ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect,
4329		    mtdPtr->virtualMenuTop, mtdPtr->virtualMenuBottom,
4330		    menuState, kThemeMenuItemScrollDownArrow, NULL, 0);
4331#ifdef TK_MAC_DEBUG_MENUS
4332	    TkMacOSXDbgMsg("downArrow: %d - %d, %d - %d", arrowRect.top,
4333		    arrowRect.bottom, arrowRect.left, arrowRect.right);
4334#endif
4335	}
4336	TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable,
4337		menuClipRect.left, menuClipRect.top, menuClipRect.right -
4338		menuClipRect.left, menuClipRect.bottom - menuClipRect.top);
4339    }
4340
4341    /*
4342     * Now, actually draw the menu. Don't draw entries that
4343     * are higher than the top arrow, and don't draw entries
4344     * that are lower than the bottom.
4345     */
4346
4347    for (i = 0; i < menuPtr->numEntries; i++) {
4348	mePtr = menuPtr->entries[i];
4349	if (mtdPtr->virtualMenuTop + mePtr->y + mePtr->height <
4350		menuClipRect.top || mtdPtr->virtualMenuTop + mePtr->y >
4351		menuClipRect.bottom) {
4352	    continue;
4353	}
4354	AppearanceEntryDrawWrapper(mePtr, menuRectPtr, mtdPtr,
4355		(Drawable) &macMDEFDrawable, &fontMetrics, menuFont, 0);
4356    }
4357    MDEFScrollFlag = 1;
4358}
4359
4360/*
4361 *----------------------------------------------------------------------
4362 *
4363 * HandleMenuFindItemMsg --
4364 *
4365 *	Handles the MenuDefProc's FindItems message. We have to
4366 *	respond by filling in the itemSelected, itemUnderMouse and
4367 *	itemRect fields. This is also the time to scroll the menu if
4368 *	it is too long to fit on the screen.
4369 *
4370 * Results:
4371 *	The Menu system is informed of the selected item & the item
4372 *	under the mouse.
4373 *
4374 * Side effects:
4375 *	The menu might get scrolled.
4376 *
4377 *----------------------------------------------------------------------
4378 */
4379void
4380HandleMenuFindItemMsg(
4381    MenuRef menu,
4382    Rect *menuRectPtr,
4383    Point hitPt,
4384    SInt16 *whichItem,
4385    TkMenu *menuPtr)
4386{
4387    Tk_Font menuFont;
4388    Tk_FontMetrics fontMetrics;
4389    TkMenuEntry *mePtr;
4390    int i, newItem = -1, itemUnderMouse = -1;
4391    Rect itemRect = {0, 0, 0, 0}, menuClipRect, bounds;
4392    int hasTopScroll, hasBottomScroll;
4393    MDEFFindItemData *fiPtr = (MDEFFindItemData *)whichItem;
4394    MenuTrackingData *mtdPtr = &(fiPtr->trackingData), topMtd;
4395    enum {
4396	DONT_SCROLL, DOWN_SCROLL, UP_SCROLL
4397    } scrollDirection;
4398    short arrowHeight;
4399
4400#ifdef TK_MAC_DEBUG_MENUS
4401    static Point lastHitPt = {0, 0};
4402    if (hitPt.h != lastHitPt.h || hitPt.v != lastHitPt.v) {
4403	lastHitPt = hitPt;
4404	TkMacOSXDbgMsg("MDEF: FindItemMsg: %d, %d", hitPt.h, hitPt.v);
4405    }
4406#endif
4407
4408    GetPort(&macMDEFDrawable.grafPtr);
4409    GetPortBounds(macMDEFDrawable.grafPtr, &bounds);
4410    macMDEFDrawable.context = (CGContextRef) fiPtr->context;
4411
4412    /*
4413     * Now we need to take care of scrolling the menu.
4414     */
4415
4416    menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
4417    Tk_GetFontMetrics(menuFont, &fontMetrics);
4418    arrowHeight = fontMetrics.linespace + 1;
4419    menuClipRect = *menuRectPtr;
4420    hasTopScroll = mtdPtr->virtualMenuTop < menuRectPtr->top;
4421    hasBottomScroll = mtdPtr->virtualMenuBottom > menuRectPtr->bottom;
4422    scrollDirection = DONT_SCROLL;
4423    if (hasTopScroll) {
4424	menuClipRect.top = bounds.top + arrowHeight;
4425	if (hitPt.v < menuClipRect.top) {
4426	    newItem = -1;
4427	    scrollDirection = DOWN_SCROLL;
4428	}
4429    }
4430    if (hasBottomScroll) {
4431	menuClipRect.bottom = bounds.bottom - 1 - arrowHeight;
4432	if (hitPt.v > menuClipRect.bottom) {
4433	    newItem = -1;
4434	    scrollDirection = UP_SCROLL;
4435	}
4436    }
4437    if (MDEFScrollFlag) {
4438	scrollDirection = DONT_SCROLL;
4439	MDEFScrollFlag = 0;
4440    }
4441    /*
4442     * Don't scroll if there are other menus open above us
4443     */
4444    ChkErr(GetMenuTrackingData, NULL, &topMtd);
4445    if (menu != topMtd.menu) {
4446	scrollDirection = DONT_SCROLL;
4447    }
4448    if (scrollDirection == DONT_SCROLL) {
4449	/*
4450	 * Find out which item was hit. If it is the same as the old item,
4451	 * we don't need to do anything.
4452	 */
4453
4454	if (PtInRect(hitPt, menuRectPtr)) {
4455	    for (i = 0; i < menuPtr->numEntries; i++) {
4456		mePtr = menuPtr->entries[i];
4457		itemRect.left = menuRectPtr->left + mePtr->x;
4458		itemRect.top = mtdPtr->virtualMenuTop + mePtr->y;
4459		itemRect.right = mePtr->entryFlags & ENTRY_LAST_COLUMN ?
4460			menuRectPtr->right : itemRect.left + mePtr->width;
4461		itemRect.bottom = itemRect.top + mePtr->height;
4462		if (PtInRect(hitPt, &itemRect)) {
4463		    if ((mePtr->type == SEPARATOR_ENTRY)
4464			    || (mePtr->state == ENTRY_DISABLED)) {
4465			newItem = -1;
4466			itemUnderMouse = i;
4467		    } else {
4468			TkMenuEntry *parentEntryPtr =
4469				GetParentMenuEntry(menuPtr);
4470
4471			if (parentEntryPtr &&
4472				parentEntryPtr->state == ENTRY_DISABLED) {
4473			    newItem = -1;
4474			    itemUnderMouse = i;
4475			} else {
4476			    newItem = i;
4477			    itemUnderMouse = i;
4478			}
4479		    }
4480		    break;
4481		}
4482	    }
4483	}
4484    } else {
4485	short scrollAmt;
4486	unsigned long scrollDelay;
4487	Rect arrowRect, eraseRect, scrolledMenuClipRect;
4488	ThemeMenuState menuState = IsMenuItemEnabled(menu, 0) ?
4489		kThemeMenuActive : kThemeMenuDisabled;
4490	int oldItem = mtdPtr->itemSelected - 1;
4491	short d;
4492
4493	TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable,
4494		0, 0, -1, -1);
4495	scrollAmt = fontMetrics.linespace + menuItemExtraHeight;
4496	if (scrollDirection == UP_SCROLL) {
4497	    scrollAmt = -scrollAmt;
4498	    d = hitPt.v - bounds.bottom;
4499	} else {
4500	    d = bounds.top - hitPt.v;
4501	}
4502	scrollDelay = (d >= scrollAmt/2) ? 1 : 10;
4503	menuClipRect = *menuRectPtr;
4504	if (mtdPtr->virtualMenuTop + scrollAmt < menuRectPtr->top) {
4505	    arrowRect = bounds;
4506	    /*arrowRect.top += 1;*/
4507	    arrowRect.bottom = arrowRect.top + arrowHeight;
4508	    eraseRect = arrowRect;
4509	    eraseRect.top = menuRectPtr->top;
4510	    menuClipRect.top = arrowRect.bottom;
4511	    if (!hasTopScroll) {
4512		ChkErr(EraseMenuBackground, menu, &eraseRect,
4513			macMDEFDrawable.context);
4514		ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect,
4515			mtdPtr->virtualMenuTop + scrollAmt,
4516			mtdPtr->virtualMenuBottom + scrollAmt,
4517			menuState, kThemeMenuItemScrollUpArrow, NULL, 0);
4518#ifdef TK_MAC_DEBUG_MENUS
4519		TkMacOSXDbgMsg("upArrow:   %d - %d, %d - %d", arrowRect.top,
4520			arrowRect.bottom, arrowRect.left, arrowRect.right);
4521#endif
4522	    }
4523	}
4524	if (mtdPtr->virtualMenuBottom + scrollAmt > menuRectPtr->bottom) {
4525	    arrowRect = bounds;
4526	    arrowRect.bottom -= 1;
4527	    arrowRect.top = arrowRect.bottom - arrowHeight;
4528	    eraseRect = arrowRect;
4529	    eraseRect.bottom = menuRectPtr->bottom;
4530	    menuClipRect.bottom = arrowRect.top;
4531	    if (!hasBottomScroll) {
4532		ChkErr(EraseMenuBackground, menu, &eraseRect,
4533			macMDEFDrawable.context);
4534		ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect,
4535			mtdPtr->virtualMenuTop + scrollAmt,
4536			mtdPtr->virtualMenuBottom + scrollAmt,
4537			menuState, kThemeMenuItemScrollDownArrow, NULL, 0);
4538#ifdef TK_MAC_DEBUG_MENUS
4539		TkMacOSXDbgMsg("downArrow: %d - %d, %d - %d", arrowRect.top,
4540			arrowRect.bottom, arrowRect.left, arrowRect.right);
4541#endif
4542	    }
4543	}
4544	TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable,
4545		menuClipRect.left, menuClipRect.top, menuClipRect.right -
4546		menuClipRect.left, menuClipRect.bottom - menuClipRect.top);
4547	TkActivateMenuEntry(menuPtr, -1);
4548	if (oldItem >= 0) {
4549	    AppearanceEntryDrawWrapper(menuPtr->entries[oldItem], menuRectPtr,
4550		    mtdPtr, (Drawable) &macMDEFDrawable, &fontMetrics,
4551		    menuFont, 1);
4552	}
4553	ChkErr(ScrollMenuImage, menu, &menuClipRect, 0, scrollAmt,
4554		macMDEFDrawable.context);
4555	mtdPtr->virtualMenuTop += scrollAmt;
4556	mtdPtr->virtualMenuBottom += scrollAmt;
4557	scrolledMenuClipRect = menuClipRect;
4558	OffsetRect(&scrolledMenuClipRect, 0, scrollAmt);
4559	menuClipRect = bounds;
4560	if (mtdPtr->virtualMenuTop < menuRectPtr->top) {
4561	    menuClipRect.top += arrowHeight;
4562	}
4563	if (mtdPtr->virtualMenuBottom > menuRectPtr->bottom) {
4564	    menuClipRect.bottom -= arrowHeight;
4565	}
4566	TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable,
4567		menuClipRect.left, menuClipRect.top, menuClipRect.right -
4568		menuClipRect.left, menuClipRect.bottom - menuClipRect.top);
4569	if (scrolledMenuClipRect.bottom < menuClipRect.bottom) {
4570	    menuClipRect.top = scrolledMenuClipRect.bottom;
4571	} else if (scrolledMenuClipRect.top < menuClipRect.top) {
4572	    menuClipRect.bottom = scrolledMenuClipRect.top;
4573	}
4574	for (i = 0; i < menuPtr->numEntries; i++) {
4575	    mePtr = menuPtr->entries[i];
4576	    if (mtdPtr->virtualMenuTop + mePtr->y + mePtr->height <
4577		    menuClipRect.top || mtdPtr->virtualMenuTop + mePtr->y >
4578		    menuClipRect.bottom) {
4579		continue;
4580	    }
4581#ifdef TK_MAC_DEBUG_MENUS
4582	    TkMacOSXDbgMsg("Drawing item %i", i);
4583#endif
4584	    AppearanceEntryDrawWrapper(mePtr, menuRectPtr, mtdPtr,
4585		    (Drawable) &macMDEFDrawable, &fontMetrics, menuFont, 1);
4586	}
4587	Delay(scrollDelay, NULL);
4588    }
4589    mtdPtr->itemSelected = newItem + 1;
4590    mtdPtr->itemUnderMouse = itemUnderMouse + 1;
4591    mtdPtr->itemRect = itemRect;
4592}
4593
4594/*
4595 *----------------------------------------------------------------------
4596 *
4597 * HandleMenuPopUpMsg --
4598 *
4599 *	Handles the MenuDefProc's PopUp message. The menu is
4600 *	posted with the selected item at the point given in hitPt.
4601 *
4602 * Results:
4603 *	A menu is posted.
4604 *
4605 * Side effects:
4606 *	None.
4607 *
4608 *----------------------------------------------------------------------
4609 */
4610void
4611HandleMenuPopUpMsg(
4612    MenuRef menu,
4613    Rect *menuRectPtr,
4614    Point hitPt,
4615    SInt16 *whichItem,
4616    TkMenu *menuPtr)
4617{
4618    int maxMenuHeight;
4619    int oldItem;
4620    Rect portRect;
4621    BitMap screenBits;
4622    static SInt16 menuBarHeight = 0;
4623
4624#ifdef TK_MAC_DEBUG_MENUS
4625    TkMacOSXDbgMsg("MDEF: PopUpMsg");
4626#endif
4627
4628    if (!menuBarHeight) {
4629	ChkErr(GetThemeMenuBarHeight, &menuBarHeight);
4630    }
4631    GetQDGlobalsScreenBits(&screenBits);
4632
4633    /*
4634     * Note that for some oddball reason, h and v are reversed in the
4635     * point given to us by the MDEF.
4636     */
4637
4638    oldItem = *whichItem;
4639    if (oldItem >= menuPtr->numEntries) {
4640	oldItem = -1;
4641    }
4642    portRect.top = 0;
4643    portRect.bottom = 1280;
4644    maxMenuHeight = screenBits.bounds.bottom - screenBits.bounds.top
4645	    - menuBarHeight - SCREEN_MARGIN;
4646    if (menuPtr->totalHeight > maxMenuHeight) {
4647	menuRectPtr->top = menuBarHeight;
4648    } else {
4649	int delta;
4650
4651	menuRectPtr->top = hitPt.h;
4652	if (oldItem >= 0) {
4653	    menuRectPtr->top -= menuPtr->entries[oldItem]->y;
4654	}
4655
4656	if (menuRectPtr->top < menuBarHeight) {
4657	    /*
4658	     * Displace downward if the menu would stick off the top of the
4659	     * screen.
4660	     */
4661
4662	    menuRectPtr->top = menuBarHeight + SCREEN_MARGIN;
4663	} else {
4664	    /*
4665	     * Or upward if the menu sticks off the bottom end...
4666	     */
4667
4668	    delta = menuRectPtr->top + menuPtr->totalHeight - maxMenuHeight;
4669	    if (delta > 0) {
4670		menuRectPtr->top -= delta;
4671	    }
4672	}
4673    }
4674    menuRectPtr->left = hitPt.v;
4675    menuRectPtr->right = menuRectPtr->left + menuPtr->totalWidth;
4676    menuRectPtr->bottom = menuRectPtr->top +
4677	    ((maxMenuHeight < menuPtr->totalHeight)
4678	    ? maxMenuHeight : menuPtr->totalHeight);
4679    if (menuRectPtr->top == menuBarHeight) {
4680	*whichItem = hitPt.h;
4681    } else {
4682	*whichItem = menuRectPtr->top;
4683    }
4684}
4685
4686/*
4687 *----------------------------------------------------------------------
4688 *
4689 * HandleMenuCalcItemMsg --
4690 *
4691 *	Handles the MenuDefProc's CalcItem message. It is supposed
4692 *	to calculate the Rect of the menu entry in whichItem in the
4693 *	menu, and put that in menuRectPtr. I assume this works, but I
4694 *	have never seen the MenuManager send this message.
4695 *
4696 * Results:
4697 *	The Menu Manager is informed of the bounding rect of a
4698 *	menu rect.
4699 *
4700 * Side effects:
4701 *	None.
4702 *
4703 *----------------------------------------------------------------------
4704 */
4705
4706void
4707HandleMenuCalcItemMsg(
4708    MenuRef menu,
4709    Rect *menuRectPtr,
4710    Point hitPt,
4711    SInt16 *whichItem,
4712    TkMenu *menuPtr)
4713{
4714    TkMenuEntry *mePtr;
4715    MenuTrackingData mtd, *mtdPtr = &mtd;
4716    OSStatus err;
4717    int virtualTop, item = *whichItem-1;
4718
4719    err = ChkErr(GetMenuTrackingData, menu, mtdPtr);
4720    if (err == noErr) {
4721	virtualTop = mtdPtr->virtualMenuTop;
4722    } else {
4723	virtualTop = 0;
4724    }
4725
4726    if (item >= 0 && item < menuPtr->numEntries) {
4727	mePtr = menuPtr->entries[item];
4728	menuRectPtr->left = mePtr->x;
4729	menuRectPtr->top = mePtr->y + virtualTop;
4730	if (mePtr->entryFlags & ENTRY_LAST_COLUMN) {
4731	    menuRectPtr->right = menuPtr->totalWidth;
4732	} else {
4733	    menuRectPtr->right = mePtr->x + mePtr->width;
4734	}
4735	menuRectPtr->bottom = menuRectPtr->top + mePtr->height;
4736    }
4737#ifdef TK_MAC_DEBUG_MENUS
4738    TkMacOSXDbgMsg("MDEF: CalcItemMsg %d: %d, %d", *whichItem,
4739	    menuRectPtr->left, menuRectPtr->top);
4740#endif
4741}
4742#endif /* USE_TK_MDEF */
4743