1/*
2 * tkWinMenu.c --
3 *
4 *	This module implements the Windows platform-specific features of menus.
5 *
6 * Copyright (c) 1996-1998 by Sun Microsystems, Inc.
7 * Copyright (c) 1998-1999 by Scriptics Corporation.
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tkWinMenu.c,v 1.21.2.9 2007/06/09 23:52:40 hobbs Exp $
13 */
14
15#define OEMRESOURCE
16#include "tkWinInt.h"
17#include "tkMenu.h"
18
19#include <string.h>
20
21/*
22 * The class of the window for popup menus.
23 */
24
25#define MENU_CLASS_NAME "MenuWindowClass"
26
27/*
28 * Used to align a windows bitmap inside a rectangle
29 */
30
31#define ALIGN_BITMAP_LEFT   0x00000001
32#define ALIGN_BITMAP_RIGHT  0x00000002
33#define ALIGN_BITMAP_TOP    0x00000004
34#define ALIGN_BITMAP_BOTTOM 0x00000008
35
36#ifndef TPM_NOANIMATION
37#define TPM_NOANIMATION 0x4000L
38#endif
39
40/*
41 * Platform-specific menu flags:
42 *
43 * MENU_SYSTEM_MENU	Non-zero means that the Windows menu handle
44 *			was retrieved with GetSystemMenu and needs
45 *			to be disposed of specially.
46 * MENU_RECONFIGURE_PENDING
47 *			Non-zero means that an idle handler has
48 *			been set up to reconfigure the Windows menu
49 *			handle for this menu.
50 */
51
52#define MENU_SYSTEM_MENU	    MENU_PLATFORM_FLAG1
53#define MENU_RECONFIGURE_PENDING    MENU_PLATFORM_FLAG2
54
55#ifndef WM_UNINITMENUPOPUP
56#define WM_UNINITMENUPOPUP              0x0125
57#endif
58
59static int indicatorDimensions[2];
60				/* The dimensions of the indicator space
61				 * in a menu entry. Calculated at init
62				 * time to save time. */
63
64typedef struct ThreadSpecificData {
65    int inPostMenu;		/* We cannot be re-entrant like X Windows. */
66    WORD lastCommandID;	        /* The last command ID we allocated. */
67    HWND menuHWND;		/* A window to service popup-menu messages
68				 * in. */
69    int oldServiceMode;	        /* Used while processing a menu; we need
70				 * to set the event mode specially when we
71				 * enter the menu processing modal loop
72				 * and reset it when menus go away. */
73    TkMenu *modalMenuPtr;	/* The menu we are processing inside the modal
74				 * loop. We need this to reset all of the
75				 * active items when menus go away since
76				 * Windows does not see fit to give this
77				 * to us when it sends its WM_MENUSELECT. */
78    Tcl_HashTable commandTable;	/* A map of command ids to menu entries */
79    Tcl_HashTable winMenuTable;	/* Need this to map HMENUs back to menuPtrs */
80} ThreadSpecificData;
81static Tcl_ThreadDataKey dataKey;
82
83/*
84 * The following are default menu value strings.
85 */
86
87static int defaultBorderWidth;	/* The windows default border width. */
88static Tcl_DString menuFontDString;
89				/* A buffer to store the default menu font
90				 * string. */
91/*
92 * Forward declarations for procedures defined later in this file:
93 */
94
95static void		DrawMenuEntryAccelerator _ANSI_ARGS_((
96			    TkMenu *menuPtr, TkMenuEntry *mePtr,
97			    Drawable d, GC gc, Tk_Font tkfont,
98			    CONST Tk_FontMetrics *fmPtr,
99			    Tk_3DBorder activeBorder, int x, int y,
100			    int width, int height));
101static void		DrawMenuEntryArrow _ANSI_ARGS_((
102			    TkMenu *menuPtr, TkMenuEntry *mePtr,
103			    Drawable d, GC gc,
104			    Tk_3DBorder activeBorder, int x, int y,
105			    int width, int height, int drawArrow));
106static void		DrawMenuEntryBackground _ANSI_ARGS_((
107			    TkMenu *menuPtr, TkMenuEntry *mePtr,
108			    Drawable d, Tk_3DBorder activeBorder,
109			    Tk_3DBorder bgBorder, int x, int y,
110			    int width, int heigth));
111static void		DrawMenuEntryIndicator _ANSI_ARGS_((
112			    TkMenu *menuPtr, TkMenuEntry *mePtr,
113			    Drawable d, GC gc, GC indicatorGC,
114			    Tk_Font tkfont,
115			    CONST Tk_FontMetrics *fmPtr, int x, int y,
116			    int width, int height));
117static void		DrawMenuEntryLabel _ANSI_ARGS_((
118			    TkMenu * menuPtr, TkMenuEntry *mePtr, Drawable d,
119			    GC gc, Tk_Font tkfont,
120			    CONST Tk_FontMetrics *fmPtr, int x, int y,
121			    int width, int height));
122static void		DrawMenuSeparator _ANSI_ARGS_((TkMenu *menuPtr,
123			    TkMenuEntry *mePtr, Drawable d, GC gc,
124			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
125			    int x, int y, int width, int height));
126static void		DrawTearoffEntry _ANSI_ARGS_((TkMenu *menuPtr,
127			    TkMenuEntry *mePtr, Drawable d, GC gc,
128			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
129			    int x, int y, int width, int height));
130static void		DrawMenuUnderline _ANSI_ARGS_((TkMenu *menuPtr,
131			    TkMenuEntry *mePtr, Drawable d, GC gc,
132			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, int x,
133			    int y, int width, int height));
134static void		DrawWindowsSystemBitmap _ANSI_ARGS_((
135			    Display *display, Drawable drawable,
136			    GC gc, CONST RECT *rectPtr, int bitmapID,
137			    int alignFlags));
138static void		FreeID _ANSI_ARGS_((WORD commandID));
139static TCHAR *		GetEntryText _ANSI_ARGS_((TkMenuEntry *mePtr));
140static void		GetMenuAccelGeometry _ANSI_ARGS_((TkMenu *menuPtr,
141			    TkMenuEntry *mePtr, Tk_Font tkfont,
142			    CONST Tk_FontMetrics *fmPtr, int *widthPtr,
143			    int *heightPtr));
144static void		GetMenuLabelGeometry _ANSI_ARGS_((TkMenuEntry *mePtr,
145			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
146			    int *widthPtr, int *heightPtr));
147static void		GetMenuIndicatorGeometry _ANSI_ARGS_((
148			    TkMenu *menuPtr, TkMenuEntry *mePtr,
149			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
150			    int *widthPtr, int *heightPtr));
151static void		GetMenuSeparatorGeometry _ANSI_ARGS_((
152			    TkMenu *menuPtr, TkMenuEntry *mePtr,
153			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
154			    int *widthPtr, int *heightPtr));
155static void		GetTearoffEntryGeometry _ANSI_ARGS_((TkMenu *menuPtr,
156			    TkMenuEntry *mePtr, Tk_Font tkfont,
157			    CONST Tk_FontMetrics *fmPtr, int *widthPtr,
158			    int *heightPtr));
159static int		GetNewID _ANSI_ARGS_((TkMenuEntry *mePtr,
160			    WORD *menuIDPtr));
161static int		MenuKeyBindProc _ANSI_ARGS_((
162			    ClientData clientData,
163			    Tcl_Interp *interp, XEvent *eventPtr,
164			    Tk_Window tkwin, KeySym keySym));
165static void		MenuSelectEvent _ANSI_ARGS_((TkMenu *menuPtr));
166static void		ReconfigureWindowsMenu _ANSI_ARGS_((
167			    ClientData clientData));
168static void		RecursivelyClearActiveMenu _ANSI_ARGS_((
169			    TkMenu *menuPtr));
170static void		SetDefaults _ANSI_ARGS_((int firstTime));
171static LRESULT CALLBACK	TkWinMenuProc _ANSI_ARGS_((HWND hwnd,
172			    UINT message, WPARAM wParam,
173			    LPARAM lParam));
174
175
176
177/*
178 *----------------------------------------------------------------------
179 *
180 * GetNewID --
181 *
182 *	Allocates a new menu id and marks it in use.
183 *
184 * Results:
185 *	Returns TCL_OK if succesful; TCL_ERROR if there are no more
186 *	ids of the appropriate type to allocate. menuIDPtr contains
187 *	the new id if succesful.
188 *
189 * Side effects:
190 *	An entry is created for the menu in the command hash table,
191 *	and the hash entry is stored in the appropriate field in the
192 *	menu data structure.
193 *
194 *----------------------------------------------------------------------
195 */
196
197static int
198GetNewID(mePtr, menuIDPtr)
199    TkMenuEntry *mePtr;		/* The menu we are working with */
200    WORD *menuIDPtr;		/* The resulting id */
201{
202    int found = 0;
203    int newEntry;
204    Tcl_HashEntry *commandEntryPtr;
205    WORD returnID;
206    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
207            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
208
209    WORD curID = tsdPtr->lastCommandID + 1;
210
211    /*
212     * The following code relies on WORD wrapping when the highest value is
213     * incremented.
214     */
215
216    while (curID != tsdPtr->lastCommandID) {
217    	commandEntryPtr = Tcl_CreateHashEntry(&tsdPtr->commandTable,
218		(char *) curID, &newEntry);
219    	if (newEntry == 1) {
220    	    found = 1;
221    	    returnID = curID;
222    	    break;
223    	}
224    	curID++;
225    }
226
227    if (found) {
228    	Tcl_SetHashValue(commandEntryPtr, (char *) mePtr);
229    	*menuIDPtr = returnID;
230    	tsdPtr->lastCommandID = returnID;
231    	return TCL_OK;
232    } else {
233    	return TCL_ERROR;
234    }
235}
236
237/*
238 *----------------------------------------------------------------------
239 *
240 * FreeID --
241 *
242 *	Marks the itemID as free.
243 *
244 * Results:
245 *	None.
246 *
247 * Side effects:
248 *	The hash table entry for the ID is cleared.
249 *
250 *----------------------------------------------------------------------
251 */
252
253static void
254FreeID(commandID)
255    WORD commandID;
256{
257    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
258            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
259
260    /*
261     * If the menuHWND is NULL, this table has been finalized already.
262     */
263
264    if (tsdPtr->menuHWND != NULL) {
265	Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&tsdPtr->commandTable,
266		(char *) commandID);
267	if (entryPtr != NULL) {
268	    Tcl_DeleteHashEntry(entryPtr);
269	}
270    }
271}
272
273/*
274 *----------------------------------------------------------------------
275 *
276 * TkpNewMenu --
277 *
278 *	Gets a new blank menu. Only the platform specific options are filled
279 *	in.
280 *
281 * Results:
282 *	Standard TCL error.
283 *
284 * Side effects:
285 *	Allocates a Windows menu handle and places it in the platformData
286 *	field of the menuPtr.
287 *
288 *----------------------------------------------------------------------
289 */
290
291int
292TkpNewMenu(menuPtr)
293    TkMenu *menuPtr;	/* The common structure we are making the
294			 * platform structure for. */
295{
296    HMENU winMenuHdl;
297    Tcl_HashEntry *hashEntryPtr;
298    int newEntry;
299    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
300            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
301
302    winMenuHdl = CreatePopupMenu();
303
304    if (winMenuHdl == NULL) {
305    	Tcl_AppendResult(menuPtr->interp, "No more menus can be allocated.",
306    		(char *) NULL);
307    	return TCL_ERROR;
308    }
309
310    /*
311     * We hash all of the HMENU's so that we can get their menu ptrs
312     * back when dispatch messages.
313     */
314
315    hashEntryPtr = Tcl_CreateHashEntry(&tsdPtr->winMenuTable,
316	    (char *) winMenuHdl, &newEntry);
317    Tcl_SetHashValue(hashEntryPtr, (char *) menuPtr);
318
319    menuPtr->platformData = (TkMenuPlatformData) winMenuHdl;
320    return TCL_OK;
321}
322
323/*
324 *----------------------------------------------------------------------
325 *
326 * TkpDestroyMenu --
327 *
328 *	Destroys platform-specific menu structures.
329 *
330 * Results:
331 *	None.
332 *
333 * Side effects:
334 *	All platform-specific allocations are freed up.
335 *
336 *----------------------------------------------------------------------
337 */
338
339void
340TkpDestroyMenu(menuPtr)
341    TkMenu *menuPtr;	    /* The common menu structure */
342{
343    HMENU winMenuHdl = (HMENU) menuPtr->platformData;
344    char *searchName;
345    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
346            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
347
348    if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
349	Tcl_CancelIdleCall(ReconfigureWindowsMenu, (ClientData) menuPtr);
350    }
351
352    if (winMenuHdl == NULL) {
353	return;
354    }
355
356    if (menuPtr->menuFlags & MENU_SYSTEM_MENU) {
357	TkMenuEntry *searchEntryPtr;
358	Tcl_HashTable *tablePtr = TkGetMenuHashTable(menuPtr->interp);
359	char *menuName = Tcl_GetHashKey(tablePtr,
360		menuPtr->menuRefPtr->hashEntryPtr);
361
362	/*
363	 * Search for the menu in the menubar, if it is present, get the
364	 * wrapper window associated with the toplevel and reset its
365	 * system menu to the default menu.
366	 */
367
368	for (searchEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
369	     searchEntryPtr != NULL;
370	     searchEntryPtr = searchEntryPtr->nextCascadePtr) {
371	    searchName = Tcl_GetStringFromObj(searchEntryPtr->namePtr, NULL);
372	    if (strcmp(searchName, menuName) == 0) {
373		Tk_Window parentTopLevelPtr = searchEntryPtr
374		    ->menuPtr->parentTopLevelPtr;
375
376		if (parentTopLevelPtr != NULL) {
377		    GetSystemMenu(TkWinGetWrapperWindow(parentTopLevelPtr),
378			    TRUE);
379		}
380		break;
381	    }
382	}
383    } else {
384	/*
385	 * Remove the menu from the menu hash table, then destroy the handle.
386	 * If the menuHWND is NULL, this table has been finalized already.
387	 */
388
389	if (tsdPtr->menuHWND != NULL) {
390	    Tcl_HashEntry *hashEntryPtr =
391		Tcl_FindHashEntry(&tsdPtr->winMenuTable, (char *) winMenuHdl);
392	    if (hashEntryPtr != NULL) {
393		Tcl_DeleteHashEntry(hashEntryPtr);
394	    }
395	}
396 	DestroyMenu(winMenuHdl);
397    }
398    menuPtr->platformData = NULL;
399
400    if (menuPtr == tsdPtr->modalMenuPtr) {
401	tsdPtr->modalMenuPtr = NULL;
402    }
403}
404
405/*
406 *----------------------------------------------------------------------
407 *
408 * TkpDestroyMenuEntry --
409 *
410 *	Cleans up platform-specific menu entry items.
411 *
412 * Results:
413 *	None
414 *
415 * Side effects:
416 *	All platform-specific allocations are freed up.
417 *
418 *----------------------------------------------------------------------
419 */
420
421void
422TkpDestroyMenuEntry(mePtr)
423    TkMenuEntry *mePtr;		    /* The entry to destroy */
424{
425    TkMenu *menuPtr = mePtr->menuPtr;
426    HMENU winMenuHdl = (HMENU) menuPtr->platformData;
427
428    if (NULL != winMenuHdl) {
429        if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
430	    menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
431	    Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
432	}
433    }
434    FreeID((WORD) mePtr->platformEntryData);
435    mePtr->platformEntryData = NULL;
436}
437
438/*
439 *----------------------------------------------------------------------
440 *
441 * GetEntryText --
442 *
443 *	Given a menu entry, gives back the text that should go in it.
444 *	Separators should be done by the caller, as they have to be
445 *	handled specially. Allocates the memory with alloc. The caller
446 *	should free the memory.
447 *
448 * Results:
449 *	itemText points to the new text for the item.
450 *
451 * Side effects:
452 *	None.
453 *
454 *----------------------------------------------------------------------
455 */
456
457static char *
458GetEntryText(mePtr)
459    TkMenuEntry *mePtr;		/* A pointer to the menu entry. */
460{
461    char *itemText;
462
463    if (mePtr->type == TEAROFF_ENTRY) {
464	itemText = ckalloc(sizeof("(Tear-off)"));
465	strcpy(itemText, "(Tear-off)");
466    } else if (mePtr->imagePtr != NULL) {
467	itemText = ckalloc(sizeof("(Image)"));
468	strcpy(itemText, "(Image)");
469    } else if (mePtr->bitmapPtr != NULL) {
470	itemText = ckalloc(sizeof("(Pixmap)"));
471	strcpy(itemText, "(Pixmap)");
472    } else if (mePtr->labelPtr == NULL || mePtr->labelLength == 0) {
473	itemText = ckalloc(sizeof("( )"));
474	strcpy(itemText, "( )");
475    } else {
476	int i;
477	char *label = (mePtr->labelPtr == NULL) ? ""
478		: Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
479	char *accel = (mePtr->accelPtr == NULL) ? ""
480		: Tcl_GetStringFromObj(mePtr->accelPtr, NULL);
481	CONST char *p, *next;
482	Tcl_DString itemString;
483
484	/*
485	 * We have to construct the string with an ampersand
486	 * preceeding the underline character, and a tab seperating
487	 * the text and the accel text. We have to be careful with
488	 * ampersands in the string.
489	 */
490
491	Tcl_DStringInit(&itemString);
492
493	for (p = label, i = 0; *p != '\0'; i++, p = next) {
494	    if (i == mePtr->underline) {
495		Tcl_DStringAppend(&itemString, "&", 1);
496	    }
497	    if (*p == '&') {
498		Tcl_DStringAppend(&itemString, "&", 1);
499	    }
500	    next = Tcl_UtfNext(p);
501	    Tcl_DStringAppend(&itemString, p, (int) (next - p));
502	}
503        if (mePtr->accelLength > 0) {
504	    Tcl_DStringAppend(&itemString, "\t", 1);
505	    for (p = accel, i = 0; *p != '\0'; i++, p = next) {
506		if (*p == '&') {
507		    Tcl_DStringAppend(&itemString, "&", 1);
508		}
509		next = Tcl_UtfNext(p);
510		Tcl_DStringAppend(&itemString, p, (int) (next - p));
511	    }
512	}
513
514	itemText = ckalloc(Tcl_DStringLength(&itemString) + 1);
515	strcpy(itemText, Tcl_DStringValue(&itemString));
516	Tcl_DStringFree(&itemString);
517    }
518    return itemText;
519}
520
521/*
522 *----------------------------------------------------------------------
523 *
524 * ReconfigureWindowsMenu --
525 *
526 *	Tears down and rebuilds the platform-specific part of this menu.
527 *
528 * Results:
529 *	None.
530 *
531 * Side effects:
532 *	Configuration information get set for mePtr; old resources
533 *	get freed, if any need it.
534 *
535 *----------------------------------------------------------------------
536 */
537
538static void
539ReconfigureWindowsMenu(
540    ClientData clientData)	    /* The menu we are rebuilding */
541{
542    TkMenu *menuPtr = (TkMenu *) clientData;
543    TkMenuEntry *mePtr;
544    HMENU winMenuHdl = (HMENU) menuPtr->platformData;
545    TCHAR *itemText = NULL;
546    const TCHAR *lpNewItem;
547    UINT flags;
548    UINT itemID;
549    int i, count, systemMenu = 0, base;
550    int width, height;
551    Tcl_DString translatedText;
552
553    if (NULL == winMenuHdl) {
554    	return;
555    }
556
557    /*
558     * Reconstruct the entire menu. Takes care of nasty system menu and index
559     * problem.
560     *
561     */
562
563    if ((menuPtr->menuType == MENUBAR)
564	    && (menuPtr->parentTopLevelPtr != NULL)) {
565	width = Tk_Width(menuPtr->parentTopLevelPtr);
566	height = Tk_Height(menuPtr->parentTopLevelPtr);
567    }
568
569    base = (menuPtr->menuFlags & MENU_SYSTEM_MENU) ? 7 : 0;
570    count = GetMenuItemCount(winMenuHdl);
571    for (i = base; i < count; i++) {
572	RemoveMenu(winMenuHdl, base, MF_BYPOSITION);
573    }
574
575    count = menuPtr->numEntries;
576    for (i = 0; i < count; i++) {
577	mePtr = menuPtr->entries[i];
578	lpNewItem = NULL;
579	flags = MF_BYPOSITION;
580	itemID = 0;
581	Tcl_DStringInit(&translatedText);
582
583	if ((menuPtr->menuType == MENUBAR) && (mePtr->type == TEAROFF_ENTRY)) {
584	    continue;
585	}
586
587	itemText = GetEntryText(mePtr);
588	if ((menuPtr->menuType == MENUBAR)
589		|| (menuPtr->menuFlags & MENU_SYSTEM_MENU)) {
590	    Tcl_WinUtfToTChar(itemText, -1, &translatedText);
591	    lpNewItem = Tcl_DStringValue(&translatedText);
592	    flags |= MF_STRING;
593	} else {
594	    lpNewItem = (LPCTSTR) mePtr;
595	    flags |= MF_OWNERDRAW;
596	}
597
598	/*
599	 * Set enabling and disabling correctly.
600	 */
601
602	if (mePtr->state == ENTRY_DISABLED) {
603	    flags |= MF_DISABLED | MF_GRAYED;
604	}
605
606	/*
607	 * Set the check mark for check entries and radio entries.
608	 */
609
610	if (((mePtr->type == CHECK_BUTTON_ENTRY)
611		|| (mePtr->type == RADIO_BUTTON_ENTRY))
612		&& (mePtr->entryFlags & ENTRY_SELECTED)) {
613	    flags |= MF_CHECKED;
614	}
615
616	/*
617	 * Set the SEPARATOR bit for separator entries.  This bit is not
618	 * used by our internal drawing functions, but it is used by the
619	 * system when drawing the system menu (we do not draw the system menu
620	 * ourselves).  If this bit is not set, separator entries on the system
621	 * menu will not be drawn correctly.
622	 */
623
624	if (mePtr->type == SEPARATOR_ENTRY) {
625	    flags |= MF_SEPARATOR;
626	}
627
628	if (mePtr->columnBreak) {
629	    flags |= MF_MENUBREAK;
630	}
631
632	itemID = (UINT) mePtr->platformEntryData;
633	if ((mePtr->type == CASCADE_ENTRY)
634		&& (mePtr->childMenuRefPtr != NULL)
635		&& (mePtr->childMenuRefPtr->menuPtr != NULL)) {
636	    HMENU childMenuHdl = (HMENU) mePtr->childMenuRefPtr->menuPtr
637		->platformData;
638	    if (childMenuHdl != NULL) {
639		/*
640		 * Win32 draws the popup arrow in the wrong color
641		 * for a disabled cascade menu, so do it by hand.
642		 * Given it is disabled, there's no need for it to
643		 * be connected to its child.
644		 */
645		if (mePtr->state != ENTRY_DISABLED) {
646		    flags |= MF_POPUP;
647		    /*
648		     * If the MF_POPUP flag is set, then the id
649		     * is interpreted as the handle of a submenu.
650		     */
651		    itemID = (UINT) childMenuHdl;
652		}
653	    }
654	    if ((menuPtr->menuType == MENUBAR)
655		    && !(mePtr->childMenuRefPtr->menuPtr->menuFlags
656			    & MENU_SYSTEM_MENU)) {
657		Tcl_DString ds;
658		TkMenuReferences *menuRefPtr;
659		TkMenu *systemMenuPtr = mePtr->childMenuRefPtr->menuPtr;
660
661		Tcl_DStringInit(&ds);
662		Tcl_DStringAppend(&ds,
663			Tk_PathName(menuPtr->masterMenuPtr->tkwin), -1);
664		Tcl_DStringAppend(&ds, ".system", 7);
665
666		menuRefPtr = TkFindMenuReferences(menuPtr->interp,
667			Tcl_DStringValue(&ds));
668
669		Tcl_DStringFree(&ds);
670
671		if ((menuRefPtr != NULL)
672			&& (menuRefPtr->menuPtr != NULL)
673			&& (menuPtr->parentTopLevelPtr != NULL)
674			&& (systemMenuPtr->masterMenuPtr
675				== menuRefPtr->menuPtr)) {
676		    HMENU systemMenuHdl =
677			(HMENU) systemMenuPtr->platformData;
678		    HWND wrapper = TkWinGetWrapperWindow(menuPtr
679			    ->parentTopLevelPtr);
680		    if (wrapper != NULL) {
681			DestroyMenu(systemMenuHdl);
682			systemMenuHdl = GetSystemMenu(wrapper, FALSE);
683			systemMenuPtr->menuFlags |= MENU_SYSTEM_MENU;
684			systemMenuPtr->platformData =
685			    (TkMenuPlatformData) systemMenuHdl;
686			if (!(systemMenuPtr->menuFlags
687				& MENU_RECONFIGURE_PENDING)) {
688			    systemMenuPtr->menuFlags
689				|= MENU_RECONFIGURE_PENDING;
690			    Tcl_DoWhenIdle(ReconfigureWindowsMenu,
691				    (ClientData) systemMenuPtr);
692			}
693		    }
694		}
695	    }
696	    if (mePtr->childMenuRefPtr->menuPtr->menuFlags
697		    & MENU_SYSTEM_MENU) {
698		systemMenu++;
699	    }
700	}
701	if (!systemMenu) {
702	    (*tkWinProcs->insertMenu)(winMenuHdl, 0xFFFFFFFF, flags,
703		    itemID, lpNewItem);
704	}
705	Tcl_DStringFree(&translatedText);
706	if (itemText != NULL) {
707	    ckfree(itemText);
708	    itemText = NULL;
709	}
710    }
711
712
713    if ((menuPtr->menuType == MENUBAR)
714	    && (menuPtr->parentTopLevelPtr != NULL)) {
715	DrawMenuBar(TkWinGetWrapperWindow(menuPtr->parentTopLevelPtr));
716	Tk_GeometryRequest(menuPtr->parentTopLevelPtr, width, height);
717    }
718
719    menuPtr->menuFlags &= ~(MENU_RECONFIGURE_PENDING);
720}
721
722/*
723 *----------------------------------------------------------------------
724 *
725 * TkpPostMenu --
726 *
727 *	Posts a menu on the screen
728 *
729 * Results:
730 *	None.
731 *
732 * Side effects:
733 *	The menu is posted and handled.
734 *
735 *----------------------------------------------------------------------
736 */
737
738int
739TkpPostMenu(interp, menuPtr, x, y)
740    Tcl_Interp *interp;
741    TkMenu *menuPtr;
742    int x;
743    int y;
744{
745    HMENU winMenuHdl = (HMENU) menuPtr->platformData;
746    int i, result, flags;
747    RECT noGoawayRect;
748    POINT point;
749    Tk_Window parentWindow = Tk_Parent(menuPtr->tkwin);
750    int oldServiceMode = Tcl_GetServiceMode();
751    TkMenuEntry *mePtr;
752    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
753            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
754
755    tsdPtr->inPostMenu++;
756
757    if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
758	Tcl_CancelIdleCall(ReconfigureWindowsMenu, (ClientData) menuPtr);
759	ReconfigureWindowsMenu((ClientData) menuPtr);
760    }
761
762    result = TkPreprocessMenu(menuPtr);
763    if (result != TCL_OK) {
764	tsdPtr->inPostMenu--;
765	return result;
766    }
767
768    /*
769     * The post commands could have deleted the menu, which means
770     * we are dead and should go away.
771     */
772
773    if (menuPtr->tkwin == NULL) {
774	tsdPtr->inPostMenu--;
775    	return TCL_OK;
776    }
777
778    if (NULL == parentWindow) {
779	noGoawayRect.top = y - 50;
780	noGoawayRect.bottom = y + 50;
781	noGoawayRect.left = x - 50;
782	noGoawayRect.right = x + 50;
783    } else {
784	int left, top;
785	Tk_GetRootCoords(parentWindow, &left, &top);
786	noGoawayRect.left = left;
787	noGoawayRect.top = top;
788	noGoawayRect.bottom = noGoawayRect.top + Tk_Height(parentWindow);
789	noGoawayRect.right = noGoawayRect.left + Tk_Width(parentWindow);
790    }
791
792    Tcl_SetServiceMode(TCL_SERVICE_NONE);
793
794    /*
795     * Make an assumption here. If the right button is down,
796     * then we want to track it. Otherwise, track the left mouse button.
797     */
798
799    flags = TPM_LEFTALIGN;
800    if (GetSystemMetrics(SM_SWAPBUTTON)) {
801	if (GetAsyncKeyState(VK_LBUTTON) < 0) {
802	    flags |= TPM_RIGHTBUTTON;
803	} else {
804	    flags |= TPM_LEFTBUTTON;
805	}
806    } else {
807	if (GetAsyncKeyState(VK_RBUTTON) < 0) {
808	    flags |= TPM_RIGHTBUTTON;
809	} else {
810	    flags |= TPM_LEFTBUTTON;
811	}
812    }
813
814    /*
815     * Disable menu animation if an image is present, as clipping isn't
816     * handled correctly with temp DCs.  [Bug 1329198]
817     */
818    for (i = 0; i < menuPtr->numEntries; i++) {
819	mePtr = menuPtr->entries[i];
820	if (mePtr->image != NULL) {
821	    flags |= TPM_NOANIMATION;
822	    break;
823	}
824    }
825
826    TrackPopupMenu(winMenuHdl, flags, x, y, 0,
827	    tsdPtr->menuHWND, &noGoawayRect);
828    Tcl_SetServiceMode(oldServiceMode);
829
830    GetCursorPos(&point);
831    Tk_PointerEvent(NULL, point.x, point.y);
832
833    if (tsdPtr->inPostMenu) {
834	tsdPtr->inPostMenu = 0;
835    }
836    return TCL_OK;
837}
838
839/*
840 *----------------------------------------------------------------------
841 *
842 * TkpMenuNewEntry --
843 *
844 *	Adds a pointer to a new menu entry structure with the platform-
845 *	specific fields filled in.
846 *
847 * Results:
848 *	Standard TCL error.
849 *
850 * Side effects:
851 *	A new command ID is allocated and stored in the platformEntryData
852 *	field of mePtr.
853 *
854 *----------------------------------------------------------------------
855 */
856
857int
858TkpMenuNewEntry(mePtr)
859    TkMenuEntry *mePtr;
860{
861    WORD commandID;
862    TkMenu *menuPtr = mePtr->menuPtr;
863
864    if (GetNewID(mePtr, &commandID) != TCL_OK) {
865    	return TCL_ERROR;
866    }
867
868    if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
869    	menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
870    	Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
871    }
872
873    mePtr->platformEntryData = (TkMenuPlatformEntryData) commandID;
874
875    return TCL_OK;
876}
877
878/*
879 *----------------------------------------------------------------------
880 *
881 * TkWinMenuProc --
882 *
883 *	The window proc for the dummy window we put popups in. This allows
884 *	is to post a popup whether or not we know what the parent window
885 *	is.
886 *
887 * Results:
888 *	Returns whatever is appropriate for the message in question.
889 *
890 * Side effects:
891 *	Normal side-effect for windows messages.
892 *
893 *----------------------------------------------------------------------
894 */
895
896static LRESULT CALLBACK
897TkWinMenuProc(hwnd, message, wParam, lParam)
898    HWND hwnd;
899    UINT message;
900    WPARAM wParam;
901    LPARAM lParam;
902{
903    LRESULT lResult;
904
905    if (!TkWinHandleMenuEvent(&hwnd, &message, &wParam, &lParam, &lResult)) {
906	lResult = DefWindowProc(hwnd, message, wParam, lParam);
907    }
908    return lResult;
909}
910
911/*
912 *----------------------------------------------------------------------
913 *
914 * TkWinHandleMenuEvent --
915 *
916 *	Filters out menu messages from messages passed to a top-level.
917 *	Will respond appropriately to WM_COMMAND, WM_MENUSELECT,
918 *	WM_MEASUREITEM, WM_DRAWITEM
919 *
920 * Result:
921 *	Returns 1 if this handled the message; 0 if it did not.
922 *
923 * Side effects:
924 *	All of the parameters may be modified so that the caller can
925 *	think it is getting a different message. plResult points to
926 *	the result that should be returned to windows from this message.
927 *
928 *----------------------------------------------------------------------
929 */
930
931int
932TkWinHandleMenuEvent(phwnd, pMessage, pwParam, plParam, plResult)
933    HWND *phwnd;
934    UINT *pMessage;
935    WPARAM *pwParam;
936    LPARAM *plParam;
937    LRESULT *plResult;
938{
939    Tcl_HashEntry *hashEntryPtr;
940    int returnResult = 0;
941    TkMenu *menuPtr;
942    TkMenuEntry *mePtr;
943    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
944            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
945
946    switch (*pMessage) {
947 	case WM_UNINITMENUPOPUP:
948	    hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable,
949		    (char *) *pwParam);
950	    if (hashEntryPtr != NULL) {
951   		menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
952		if ((menuPtr->menuRefPtr != NULL)
953			&& (menuPtr->menuRefPtr->parentEntryPtr != NULL)) {
954		    TkPostSubmenu(menuPtr->interp,
955			    menuPtr->menuRefPtr->parentEntryPtr->menuPtr, NULL);
956		}
957	    }
958	    break;
959
960	case WM_INITMENU:
961	    TkMenuInit();
962	    hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable,
963                    (char *) *pwParam);
964	    if (hashEntryPtr != NULL) {
965		tsdPtr->oldServiceMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
966		menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
967		tsdPtr->modalMenuPtr = menuPtr;
968		if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
969		    Tcl_CancelIdleCall(ReconfigureWindowsMenu,
970			    (ClientData) menuPtr);
971		    ReconfigureWindowsMenu((ClientData) menuPtr);
972		}
973		RecursivelyClearActiveMenu(menuPtr);
974		if (!tsdPtr->inPostMenu) {
975		    Tcl_Interp *interp;
976		    int code;
977
978		    interp = menuPtr->interp;
979		    Tcl_Preserve((ClientData)interp);
980		    code = TkPreprocessMenu(menuPtr);
981		    if ((code != TCL_OK) && (code != TCL_CONTINUE)
982			    && (code != TCL_BREAK)) {
983			Tcl_AddErrorInfo(interp, "\n    (menu preprocess)");
984			Tcl_BackgroundError(interp);
985		    }
986		    Tcl_Release((ClientData)interp);
987		}
988		TkActivateMenuEntry(menuPtr, -1);
989		*plResult = 0;
990		returnResult = 1;
991	    } else {
992		tsdPtr->modalMenuPtr = NULL;
993	    }
994	    break;
995
996	case WM_SYSCOMMAND:
997	case WM_COMMAND: {
998	    TkMenuInit();
999	    if (HIWORD(*pwParam) != 0) {
1000		break;
1001	    }
1002	    hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->commandTable,
1003		    (char *)LOWORD(*pwParam));
1004	    if (hashEntryPtr == NULL) {
1005		break;
1006	    }
1007	    mePtr = (TkMenuEntry *) Tcl_GetHashValue(hashEntryPtr);
1008	    if (mePtr != NULL) {
1009		TkMenuReferences *menuRefPtr;
1010		TkMenuEntry *parentEntryPtr;
1011		Tcl_Interp *interp;
1012		int code;
1013
1014		/*
1015		 * We have to set the parent of this menu to be active
1016		 * if this is a submenu so that tearoffs will get the
1017		 * correct title.
1018		 */
1019
1020		menuPtr = mePtr->menuPtr;
1021		menuRefPtr = TkFindMenuReferences(menuPtr->interp,
1022			Tk_PathName(menuPtr->tkwin));
1023		if ((menuRefPtr != NULL)
1024			&& (menuRefPtr->parentEntryPtr != NULL)) {
1025		    char *name;
1026
1027		    for (parentEntryPtr = menuRefPtr->parentEntryPtr;
1028			 ;
1029			 parentEntryPtr =
1030			     parentEntryPtr->nextCascadePtr) {
1031			name = Tcl_GetStringFromObj(
1032			    parentEntryPtr->namePtr, NULL);
1033			if (strcmp(name, Tk_PathName(menuPtr->tkwin))
1034				== 0) {
1035			    break;
1036			}
1037		    }
1038		    if (parentEntryPtr->menuPtr->entries[parentEntryPtr->index]
1039			    ->state != ENTRY_DISABLED) {
1040			TkActivateMenuEntry(parentEntryPtr->menuPtr,
1041				parentEntryPtr->index);
1042		    }
1043		}
1044
1045		interp = menuPtr->interp;
1046		Tcl_Preserve((ClientData)interp);
1047		code = TkInvokeMenu(interp, menuPtr, mePtr->index);
1048		if (code != TCL_OK && code != TCL_CONTINUE
1049			&& code != TCL_BREAK) {
1050		    Tcl_AddErrorInfo(interp, "\n    (menu invoke)");
1051		    Tcl_BackgroundError(interp);
1052		}
1053		Tcl_Release((ClientData)interp);
1054	    }
1055	    *plResult = 0;
1056	    returnResult = 1;
1057	    break;
1058	}
1059
1060
1061	case WM_MENUCHAR: {
1062	    hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable,
1063		    (char *) *plParam);
1064	    if (hashEntryPtr != NULL) {
1065		int i, len, underline;
1066		Tcl_Obj *labelPtr;
1067		Tcl_UniChar *wlabel, menuChar;
1068
1069		*plResult = 0;
1070		menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
1071		/*
1072		 * Assume we have something directly convertable to
1073		 * Tcl_UniChar.  True at least for wide systems.
1074		 */
1075		menuChar = Tcl_UniCharToUpper((Tcl_UniChar) LOWORD(*pwParam));
1076
1077		for (i = 0; i < menuPtr->numEntries; i++) {
1078		    underline = menuPtr->entries[i]->underline;
1079		    labelPtr = menuPtr->entries[i]->labelPtr;
1080		    if ((underline >= 0) && (labelPtr != NULL)) {
1081			/*
1082			 * Ensure we don't exceed the label length, then check
1083			 */
1084			wlabel = Tcl_GetUnicodeFromObj(labelPtr, &len);
1085			if ((underline < len) && (menuChar ==
1086				    Tcl_UniCharToUpper(wlabel[underline]))) {
1087			    *plResult = (2 << 16) | i;
1088			    returnResult = 1;
1089			    break;
1090			}
1091		    }
1092		}
1093	    }
1094	    break;
1095	}
1096
1097	case WM_MEASUREITEM: {
1098	    LPMEASUREITEMSTRUCT itemPtr = (LPMEASUREITEMSTRUCT) *plParam;
1099
1100	    if (itemPtr != NULL) {
1101		mePtr = (TkMenuEntry *) itemPtr->itemData;
1102		menuPtr = mePtr->menuPtr;
1103
1104		TkRecomputeMenu(menuPtr);
1105		itemPtr->itemHeight = mePtr->height;
1106		itemPtr->itemWidth = mePtr->width;
1107		if (mePtr->hideMargin) {
1108		    itemPtr->itemWidth += 2 - indicatorDimensions[1];
1109		} else {
1110		    int activeBorderWidth;
1111
1112		    Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin,
1113			    menuPtr->activeBorderWidthPtr,
1114			    &activeBorderWidth);
1115		    itemPtr->itemWidth += 2 * activeBorderWidth;
1116		}
1117		*plResult = 1;
1118		returnResult = 1;
1119	    }
1120	    break;
1121	}
1122
1123	case WM_DRAWITEM: {
1124	    TkWinDrawable *twdPtr;
1125	    LPDRAWITEMSTRUCT itemPtr = (LPDRAWITEMSTRUCT) *plParam;
1126	    Tk_FontMetrics fontMetrics;
1127	    int drawArrow = 0;
1128
1129	    if (itemPtr != NULL) {
1130		Tk_Font tkfont;
1131
1132		mePtr = (TkMenuEntry *) itemPtr->itemData;
1133		menuPtr = mePtr->menuPtr;
1134		twdPtr = (TkWinDrawable *) ckalloc(sizeof(TkWinDrawable));
1135		twdPtr->type = TWD_WINDC;
1136		twdPtr->winDC.hdc = itemPtr->hDC;
1137
1138		if (mePtr->state != ENTRY_DISABLED) {
1139		    if (itemPtr->itemState & ODS_SELECTED) {
1140			TkActivateMenuEntry(menuPtr, mePtr->index);
1141		    } else {
1142			TkActivateMenuEntry(menuPtr, -1);
1143		    }
1144		} else {
1145		    /* On windows, menu entries should highlight even if they
1146		    ** are disabled. (I know this seems dumb, but it is the way
1147		    ** native windows menus works so we ought to mimic it.)
1148		    ** The ENTRY_PLATFORM_FLAG1 flag will indicate that the
1149		    ** entry should be highlighted even though it is disabled.
1150		    */
1151		    if (itemPtr->itemState & ODS_SELECTED) {
1152			mePtr->entryFlags |= ENTRY_PLATFORM_FLAG1;
1153		    } else {
1154			mePtr->entryFlags &= ~ENTRY_PLATFORM_FLAG1;
1155		    }
1156		    /* Also, set the drawArrow flag for a disabled cascade
1157		    ** menu since we need to draw the arrow ourselves.
1158		    */
1159		    if (mePtr->type == CASCADE_ENTRY) {
1160		        drawArrow = 1;
1161		    }
1162		}
1163
1164		tkfont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
1165		Tk_GetFontMetrics(tkfont, &fontMetrics);
1166		TkpDrawMenuEntry(mePtr, (Drawable) twdPtr, tkfont,
1167			&fontMetrics, itemPtr->rcItem.left,
1168			itemPtr->rcItem.top, itemPtr->rcItem.right
1169			- itemPtr->rcItem.left, itemPtr->rcItem.bottom
1170			- itemPtr->rcItem.top, 0, drawArrow);
1171
1172		ckfree((char *) twdPtr);
1173		*plResult = 1;
1174		returnResult = 1;
1175	    }
1176	    break;
1177	}
1178
1179	case WM_MENUSELECT: {
1180	    UINT flags = HIWORD(*pwParam);
1181
1182	    TkMenuInit();
1183
1184	    if ((flags == 0xFFFF) && (*plParam == 0)) {
1185		if (tsdPtr->modalMenuPtr != NULL) {
1186		    Tcl_SetServiceMode(tsdPtr->oldServiceMode);
1187		    RecursivelyClearActiveMenu(tsdPtr->modalMenuPtr);
1188		}
1189	    } else {
1190		menuPtr = NULL;
1191 		if (*plParam != 0) {
1192		    hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable,
1193			    (char *) *plParam);
1194		    if (hashEntryPtr != NULL) {
1195			menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
1196		    }
1197		}
1198
1199		if (menuPtr != NULL) {
1200	    	    mePtr = NULL;
1201		    if (flags != 0xFFFF) {
1202			if (flags & MF_POPUP) {
1203			    mePtr = menuPtr->entries[LOWORD(*pwParam)];
1204			} else {
1205			    hashEntryPtr = Tcl_FindHashEntry(
1206				&tsdPtr->commandTable,
1207				(char *) LOWORD(*pwParam));
1208			    if (hashEntryPtr != NULL) {
1209				mePtr = (TkMenuEntry *)
1210				    Tcl_GetHashValue(hashEntryPtr);
1211			    }
1212			}
1213		    }
1214
1215		    if ((mePtr == NULL) || (mePtr->state == ENTRY_DISABLED)) {
1216			TkActivateMenuEntry(menuPtr, -1);
1217		    } else {
1218			if (mePtr->index >= menuPtr->numEntries) {
1219			    Tcl_Panic("Trying to activate an entry which doesn't exist.");
1220			}
1221			TkActivateMenuEntry(menuPtr, mePtr->index);
1222		    }
1223		    MenuSelectEvent(menuPtr);
1224		    Tcl_ServiceAll();
1225		}
1226	    }
1227	    break;
1228	}
1229    }
1230    return returnResult;
1231}
1232
1233/*
1234 *----------------------------------------------------------------------
1235 *
1236 * RecursivelyClearActiveMenu --
1237 *
1238 *	Recursively clears the active entry in the menu's cascade hierarchy.
1239 *
1240 * Results:
1241 *	None.
1242 *
1243 * Side effects:
1244 *	Generates <<MenuSelect>> virtual events.
1245 *
1246 *----------------------------------------------------------------------
1247 */
1248
1249void
1250RecursivelyClearActiveMenu(
1251    TkMenu *menuPtr)		/* The menu to reset. */
1252{
1253    int i;
1254    TkMenuEntry *mePtr;
1255
1256    TkActivateMenuEntry(menuPtr, -1);
1257    MenuSelectEvent(menuPtr);
1258    for (i = 0; i < menuPtr->numEntries; i++) {
1259    	mePtr = menuPtr->entries[i];
1260	if (mePtr->state == ENTRY_ACTIVE) {
1261	    mePtr->state = ENTRY_NORMAL;
1262	}
1263	mePtr->entryFlags &= ~ENTRY_PLATFORM_FLAG1;
1264    	if (mePtr->type == CASCADE_ENTRY) {
1265    	    if ((mePtr->childMenuRefPtr != NULL)
1266    	    	    && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
1267    	    	RecursivelyClearActiveMenu(mePtr->childMenuRefPtr->menuPtr);
1268    	    }
1269    	}
1270    }
1271}
1272
1273/*
1274 *----------------------------------------------------------------------
1275 *
1276 * TkpSetWindowMenuBar --
1277 *
1278 *	Associates a given menu with a window.
1279 *
1280 * Results:
1281 *	None.
1282 *
1283 * Side effects:
1284 *	On Windows and UNIX, associates the platform menu with the
1285 *	platform window.
1286 *
1287 *----------------------------------------------------------------------
1288 */
1289
1290void
1291TkpSetWindowMenuBar(tkwin, menuPtr)
1292    Tk_Window tkwin;	    /* The window we are putting the menubar into.*/
1293    TkMenu *menuPtr;	    /* The menu we are inserting */
1294{
1295    HMENU winMenuHdl;
1296    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1297	Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1298
1299    if (menuPtr != NULL) {
1300	Tcl_HashEntry *hashEntryPtr;
1301	int newEntry;
1302
1303	winMenuHdl = (HMENU) menuPtr->platformData;
1304	hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable,
1305                (char *) winMenuHdl);
1306	Tcl_DeleteHashEntry(hashEntryPtr);
1307	DestroyMenu(winMenuHdl);
1308	winMenuHdl = CreateMenu();
1309	hashEntryPtr = Tcl_CreateHashEntry(&tsdPtr->winMenuTable,
1310                (char *) winMenuHdl, &newEntry);
1311	Tcl_SetHashValue(hashEntryPtr, (char *) menuPtr);
1312	menuPtr->platformData = (TkMenuPlatformData) winMenuHdl;
1313	TkWinSetMenu(tkwin, winMenuHdl);
1314	if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
1315	    menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
1316	    Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
1317	}
1318    } else {
1319	TkWinSetMenu(tkwin, NULL);
1320    }
1321}
1322
1323/*
1324 *----------------------------------------------------------------------
1325 *
1326 * TkpSetMainMenubar --
1327 *
1328 *	Puts the menu associated with a window into the menubar. Should
1329 *	only be called when the window is in front.
1330 *
1331 * Results:
1332 *	None.
1333 *
1334 * Side effects:
1335 *	The menubar is changed.
1336 *
1337 *----------------------------------------------------------------------
1338 */
1339void
1340TkpSetMainMenubar(
1341    Tcl_Interp *interp,		/* The interpreter of the application */
1342    Tk_Window tkwin,		/* The frame we are setting up */
1343    char *menuName)		/* The name of the menu to put in front.
1344    				 * If NULL, use the default menu bar.
1345    				 */
1346{
1347    /*
1348     * Nothing to do.
1349     */
1350}
1351
1352/*
1353 *----------------------------------------------------------------------
1354 *
1355 * GetMenuIndicatorGeometry --
1356 *
1357 *	Gets the width and height of the indicator area of a menu.
1358 *
1359 * Results:
1360 *	widthPtr and heightPtr are set.
1361 *
1362 * Side effects:
1363 *	None.
1364 *
1365 *----------------------------------------------------------------------
1366 */
1367
1368void
1369GetMenuIndicatorGeometry (
1370    TkMenu *menuPtr,			/* The menu we are measuring */
1371    TkMenuEntry *mePtr,			/* The entry we are measuring */
1372    Tk_Font tkfont,			/* Precalculated font */
1373    CONST Tk_FontMetrics *fmPtr,	/* Precalculated font metrics */
1374    int *widthPtr,			/* The resulting width */
1375    int *heightPtr)			/* The resulting height */
1376{
1377    *heightPtr = indicatorDimensions[0];
1378    if (mePtr->hideMargin) {
1379	*widthPtr = 0;
1380    } else {
1381	int borderWidth;
1382
1383	Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin,
1384		menuPtr->borderWidthPtr, &borderWidth);
1385	*widthPtr = indicatorDimensions[1] - borderWidth;
1386    }
1387}
1388
1389/*
1390 *----------------------------------------------------------------------
1391 *
1392 * GetMenuAccelGeometry --
1393 *
1394 *	Gets the width and height of the indicator area of a menu.
1395 *
1396 * Results:
1397 *	widthPtr and heightPtr are set.
1398 *
1399 * Side effects:
1400 *	None.
1401 *
1402 *----------------------------------------------------------------------
1403 */
1404
1405void
1406GetMenuAccelGeometry (
1407    TkMenu *menuPtr,			/* The menu we are measuring */
1408    TkMenuEntry *mePtr,			/* The entry we are measuring */
1409    Tk_Font tkfont,			/* The precalculated font */
1410    CONST Tk_FontMetrics *fmPtr,	/* The precalculated font metrics */
1411    int *widthPtr,			/* The resulting width */
1412    int *heightPtr)			/* The resulting height */
1413{
1414    *heightPtr = fmPtr->linespace;
1415    if (mePtr->type == CASCADE_ENTRY) {
1416	*widthPtr = 0;
1417    } else if (mePtr->accelPtr == NULL) {
1418	*widthPtr = 0;
1419    } else {
1420	char *accel = Tcl_GetStringFromObj(mePtr->accelPtr, NULL);
1421	*widthPtr = Tk_TextWidth(tkfont, accel, mePtr->accelLength);
1422    }
1423}
1424
1425/*
1426 *----------------------------------------------------------------------
1427 *
1428 * GetTearoffEntryGeometry --
1429 *
1430 *	Gets the width and height of the indicator area of a menu.
1431 *
1432 * Results:
1433 *	widthPtr and heightPtr are set.
1434 *
1435 * Side effects:
1436 *	None.
1437 *
1438 *----------------------------------------------------------------------
1439 */
1440
1441void
1442GetTearoffEntryGeometry (
1443    TkMenu *menuPtr,			/* The menu we are measuring */
1444    TkMenuEntry *mePtr,			/* The entry we are measuring */
1445    Tk_Font tkfont,			/* The precalculated font */
1446    CONST Tk_FontMetrics *fmPtr,	/* The precalculated font metrics */
1447    int *widthPtr,			/* The resulting width */
1448    int *heightPtr)			/* The resulting height */
1449{
1450    if (menuPtr->menuType != MASTER_MENU) {
1451	*heightPtr = 0;
1452    } else {
1453	*heightPtr = fmPtr->linespace;
1454    }
1455    *widthPtr = 0;
1456}
1457
1458/*
1459 *----------------------------------------------------------------------
1460 *
1461 * GetMenuSeparatorGeometry --
1462 *
1463 *	Gets the width and height of the indicator area of a menu.
1464 *
1465 * Results:
1466 *	widthPtr and heightPtr are set.
1467 *
1468 * Side effects:
1469 *	None.
1470 *
1471 *----------------------------------------------------------------------
1472 */
1473
1474void
1475GetMenuSeparatorGeometry (
1476    TkMenu *menuPtr,			/* The menu we are measuring */
1477    TkMenuEntry *mePtr,			/* The entry we are measuring */
1478    Tk_Font tkfont,			/* The precalculated font */
1479    CONST Tk_FontMetrics *fmPtr,	/* The precalcualted font metrics */
1480    int *widthPtr,			/* The resulting width */
1481    int *heightPtr)			/* The resulting height */
1482{
1483    *widthPtr = 0;
1484    *heightPtr = fmPtr->linespace - (2 * fmPtr->descent);
1485}
1486
1487/*
1488 *----------------------------------------------------------------------
1489 *
1490 * DrawWindowsSystemBitmap --
1491 *
1492 *	Draws the windows system bitmap given by bitmapID into the rect
1493 *	given by rectPtr in the drawable. The bitmap is centered in the
1494 *	rectangle. It is not clipped, so if the bitmap is bigger than
1495 *	the rect it will bleed.
1496 *
1497 * Results:
1498 *	None.
1499 *
1500 * Side effects:
1501 *	Drawing occurs. Some storage is allocated and released.
1502 *
1503 *----------------------------------------------------------------------
1504 */
1505
1506static void
1507DrawWindowsSystemBitmap(display, drawable, gc, rectPtr, bitmapID, alignFlags)
1508    Display *display;			/* The display we are drawing into */
1509    Drawable drawable;			/* The drawable we are working with */
1510    GC gc;				/* The GC to draw with */
1511    CONST RECT *rectPtr;		/* The rectangle to draw into */
1512    int bitmapID;			/* The windows id of the system
1513					 * bitmap to draw. */
1514    int alignFlags;			/* How to align the bitmap inside the
1515					 * rectangle. */
1516{
1517    TkWinDCState state;
1518    HDC hdc = TkWinGetDrawableDC(display, drawable, &state);
1519    HDC scratchDC;
1520    HBITMAP bitmap;
1521    BITMAP bm;
1522    POINT ptSize;
1523    POINT ptOrg;
1524    int topOffset, leftOffset;
1525
1526    SetBkColor(hdc, gc->background);
1527    SetTextColor(hdc, gc->foreground);
1528
1529    scratchDC = CreateCompatibleDC(hdc);
1530    bitmap = LoadBitmap(NULL, MAKEINTRESOURCE(bitmapID));
1531
1532    SelectObject(scratchDC, bitmap);
1533    SetMapMode(scratchDC, GetMapMode(hdc));
1534    GetObject(bitmap, sizeof(BITMAP), &bm);
1535    ptSize.x = bm.bmWidth;
1536    ptSize.y = bm.bmHeight;
1537    DPtoLP(scratchDC, &ptSize, 1);
1538
1539    ptOrg.y = ptOrg.x = 0;
1540    DPtoLP(scratchDC, &ptOrg, 1);
1541
1542    if (alignFlags & ALIGN_BITMAP_TOP) {
1543	topOffset = 0;
1544    } else if (alignFlags & ALIGN_BITMAP_BOTTOM) {
1545	topOffset = (rectPtr->bottom - rectPtr->top) - ptSize.y;
1546    } else {
1547	topOffset = (rectPtr->bottom - rectPtr->top) / 2 - (ptSize.y / 2);
1548    }
1549
1550    if (alignFlags & ALIGN_BITMAP_LEFT) {
1551	leftOffset = 0;
1552    } else if (alignFlags & ALIGN_BITMAP_RIGHT) {
1553	leftOffset = (rectPtr->right - rectPtr->left) - ptSize.x;
1554    } else {
1555	leftOffset = (rectPtr->right - rectPtr->left) / 2 - (ptSize.x / 2);
1556    }
1557
1558    BitBlt(hdc, rectPtr->left + leftOffset, rectPtr->top + topOffset, ptSize.x,
1559	    ptSize.y, scratchDC, ptOrg.x, ptOrg.y, SRCCOPY);
1560    DeleteDC(scratchDC);
1561    DeleteObject(bitmap);
1562
1563    TkWinReleaseDrawableDC(drawable, hdc, &state);
1564}
1565
1566/*
1567 *----------------------------------------------------------------------
1568 *
1569 * DrawMenuEntryIndicator --
1570 *
1571 *	This procedure draws the indicator part of a menu.
1572 *
1573 * Results:
1574 *	None.
1575 *
1576 * Side effects:
1577 *	Commands are output to X to display the menu in its
1578 *	current mode.
1579 *
1580 *----------------------------------------------------------------------
1581 */
1582void
1583DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont, fmPtr, x,
1584	y, width, height)
1585    TkMenu *menuPtr;		    /* The menu we are drawing */
1586    TkMenuEntry *mePtr;		    /* The entry we are drawing */
1587    Drawable d;			    /* What we are drawing into */
1588    GC gc;			    /* The gc we are drawing with */
1589    GC indicatorGC;		    /* The gc for indicator objects */
1590    Tk_Font tkfont;		    /* The precalculated font */
1591    CONST Tk_FontMetrics *fmPtr;    /* The precalculated font metrics */
1592    int x;			    /* Left edge */
1593    int y;			    /* Top edge */
1594    int width;
1595    int height;
1596{
1597    if ((mePtr->type == CHECK_BUTTON_ENTRY)
1598	    || (mePtr->type == RADIO_BUTTON_ENTRY)) {
1599    	if (mePtr->indicatorOn && (mePtr->entryFlags & ENTRY_SELECTED)) {
1600	    RECT rect;
1601	    GC whichGC;
1602	    int borderWidth, activeBorderWidth;
1603	    if (mePtr->state != ENTRY_NORMAL) {
1604		whichGC = gc;
1605	    } else {
1606		whichGC = indicatorGC;
1607	    }
1608
1609	    rect.top = y;
1610	    rect.bottom = y + mePtr->height;
1611	    Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin,
1612		    menuPtr->borderWidthPtr, &borderWidth);
1613	    Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin,
1614		    menuPtr->activeBorderWidthPtr, &activeBorderWidth);
1615	    rect.left = borderWidth + activeBorderWidth + x;
1616	    rect.right = mePtr->indicatorSpace + x;
1617
1618	    if ((mePtr->state == ENTRY_DISABLED)
1619		    && (menuPtr->disabledFgPtr != NULL)) {
1620		RECT hilightRect;
1621		COLORREF oldFgColor = whichGC->foreground;
1622
1623		whichGC->foreground = GetSysColor(COLOR_3DHILIGHT);
1624		hilightRect.top = rect.top + 1;
1625		hilightRect.bottom = rect.bottom + 1;
1626		hilightRect.left = rect.left + 1;
1627		hilightRect.right = rect.right + 1;
1628		DrawWindowsSystemBitmap(menuPtr->display, d, whichGC,
1629			&hilightRect, OBM_CHECK, 0);
1630		whichGC->foreground = oldFgColor;
1631	    }
1632
1633	    DrawWindowsSystemBitmap(menuPtr->display, d, whichGC, &rect,
1634		    OBM_CHECK, 0);
1635	}
1636    }
1637}
1638
1639/*
1640 *----------------------------------------------------------------------
1641 *
1642 * DrawMenuEntryAccelerator --
1643 *
1644 *	This procedure draws the accelerator part of a menu.
1645 *	For example, the string "CTRL-Z" could be drawn to
1646 *	to the right of the label text for an Undo menu entry.
1647 *	Need to decide what to draw here. Should we replace strings
1648 *	like "Control", "Command", etc?
1649 *
1650 * Results:
1651 *	None.
1652 *
1653 * Side effects:
1654 *	Commands are output to display the menu in its
1655 *	current mode.
1656 *
1657 *----------------------------------------------------------------------
1658 */
1659
1660void
1661DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
1662	activeBorder, x, y, width, height)
1663    TkMenu *menuPtr;			/* The menu we are drawing */
1664    TkMenuEntry *mePtr;			/* The entry we are drawing */
1665    Drawable d;				/* What we are drawing into */
1666    GC gc;				/* The gc we are drawing with */
1667    Tk_Font tkfont;			/* The precalculated font */
1668    CONST Tk_FontMetrics *fmPtr;	/* The precalculated font metrics */
1669    Tk_3DBorder activeBorder;		/* The border when an item is active */
1670    int x;				/* left edge */
1671    int y;				/* top edge */
1672    int width;				/* Width of menu entry */
1673    int height;				/* Height of menu entry */
1674{
1675    int baseline;
1676    int leftEdge = x + mePtr->indicatorSpace + mePtr->labelWidth;
1677    char *accel;
1678
1679    if (mePtr->accelPtr != NULL) {
1680	accel = Tcl_GetStringFromObj(mePtr->accelPtr, NULL);
1681    }
1682
1683    baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
1684
1685    /* Draw disabled 3D text highlight only with the Win95/98 look. */
1686
1687    if (TkWinGetPlatformTheme() == TK_THEME_WIN_CLASSIC) {
1688	if ((mePtr->state == ENTRY_DISABLED) && (menuPtr->disabledFgPtr != NULL)
1689	        && (mePtr->accelPtr != NULL)) {
1690	    COLORREF oldFgColor = gc->foreground;
1691
1692	    gc->foreground = GetSysColor(COLOR_3DHILIGHT);
1693	    if ((mePtr->accelPtr != NULL) &&
1694	            ((mePtr->entryFlags & ENTRY_PLATFORM_FLAG1) == 0)) {
1695	        Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel,
1696		        mePtr->accelLength, leftEdge + 1, baseline + 1);
1697	    }
1698	    gc->foreground = oldFgColor;
1699	}
1700    }
1701
1702    if (mePtr->accelPtr != NULL) {
1703	Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel,
1704		mePtr->accelLength, leftEdge, baseline);
1705    }
1706}
1707
1708/*
1709 *----------------------------------------------------------------------
1710 *
1711 * DrawMenuEntryArrow --
1712 *
1713 *	This function draws the arrow bitmap on the right side of a
1714 *	a menu entry. This function is currently unused.
1715 *
1716 * Results:
1717 *	None.
1718 *
1719 * Side effects:
1720 *	None.
1721 *
1722 *----------------------------------------------------------------------
1723 */
1724
1725void
1726DrawMenuEntryArrow(menuPtr, mePtr, d, gc,
1727	activeBorder, x, y, width, height, drawArrow)
1728    TkMenu *menuPtr;			/* The menu we are drawing */
1729    TkMenuEntry *mePtr;			/* The entry we are drawing */
1730    Drawable d;				/* What we are drawing into */
1731    GC gc;				/* The gc we are drawing with */
1732    Tk_3DBorder activeBorder;		/* The border when an item is active */
1733    int x;				/* left edge */
1734    int y;				/* top edge */
1735    int width;				/* Width of menu entry */
1736    int height;				/* Height of menu entry */
1737    int drawArrow;			/* For cascade menus, whether of not
1738					 * to draw the arraw. I cannot figure
1739					 * out Windows' algorithm for where
1740					 * to draw this. */
1741{
1742    COLORREF oldFgColor;
1743    COLORREF oldBgColor;
1744    RECT rect;
1745
1746    if (!drawArrow || (mePtr->type != CASCADE_ENTRY))
1747        return;
1748
1749    oldFgColor = gc->foreground;
1750    oldBgColor = gc->background;
1751
1752    /* Set bitmap bg to highlight color if the menu is highlighted */
1753    if (mePtr->entryFlags & ENTRY_PLATFORM_FLAG1) {
1754        XColor *activeBgColor = Tk_3DBorderColor(Tk_Get3DBorderFromObj(
1755						     mePtr->menuPtr->tkwin,
1756						     (mePtr->activeBorderPtr == NULL) ?
1757						     mePtr->menuPtr->activeBorderPtr :
1758						     mePtr->activeBorderPtr));
1759        gc->background = activeBgColor->pixel;
1760    }
1761
1762    gc->foreground = GetSysColor((mePtr->state == ENTRY_DISABLED) ?
1763	    COLOR_GRAYTEXT : COLOR_MENUTEXT);
1764
1765    rect.top = y + GetSystemMetrics(SM_CYBORDER);
1766    rect.bottom = y + height - GetSystemMetrics(SM_CYBORDER);
1767    rect.left = x + mePtr->indicatorSpace + mePtr->labelWidth;
1768    rect.right = x + width;
1769
1770    DrawWindowsSystemBitmap(menuPtr->display, d, gc, &rect, OBM_MNARROW,
1771            ALIGN_BITMAP_RIGHT);
1772
1773    gc->foreground = oldFgColor;
1774    gc->background = oldBgColor;
1775    return;
1776}
1777
1778/*
1779 *----------------------------------------------------------------------
1780 *
1781 * DrawMenuSeparator --
1782 *
1783 *	The menu separator is drawn.
1784 *
1785 * Results:
1786 *	None.
1787 *
1788 * Side effects:
1789 *	Commands are output to X to display the menu in its
1790 *	current mode.
1791 *
1792 *----------------------------------------------------------------------
1793 */
1794void
1795DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
1796    TkMenu *menuPtr;			/* The menu we are drawing */
1797    TkMenuEntry *mePtr;			/* The entry we are drawing */
1798    Drawable d;				/* What we are drawing into */
1799    GC gc;				/* The gc we are drawing with */
1800    Tk_Font tkfont;			/* The precalculated font */
1801    CONST Tk_FontMetrics *fmPtr;	/* The precalculated font metrics */
1802    int x;				/* left edge */
1803    int y;				/* top edge */
1804    int width;				/* width of item */
1805    int height;				/* height of item */
1806{
1807    XPoint points[2];
1808    Tk_3DBorder border;
1809
1810    points[0].x = x;
1811    points[0].y = y + height / 2;
1812    points[1].x = x + width - 1;
1813    points[1].y = points[0].y;
1814    border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
1815    Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1,
1816	    TK_RELIEF_RAISED);
1817}
1818
1819/*
1820 *----------------------------------------------------------------------
1821 *
1822 * DrawMenuUnderline --
1823 *
1824 *	On appropriate platforms, draw the underline character for the menu.
1825 *
1826 * Results:
1827 *	None.
1828 *
1829 * Side effects:
1830 *	Commands are output to X to display the menu in its
1831 *	current mode.
1832 *
1833 *----------------------------------------------------------------------
1834 */
1835static void
1836DrawMenuUnderline(
1837    TkMenu *menuPtr,			/* The menu to draw into */
1838    TkMenuEntry *mePtr,			/* The entry we are drawing */
1839    Drawable d,				/* What we are drawing into */
1840    GC gc,				/* The gc to draw into */
1841    Tk_Font tkfont,			/* The precalculated font */
1842    CONST Tk_FontMetrics *fmPtr,	/* The precalculated font metrics */
1843    int x,				/* Left Edge */
1844    int y,				/* Top Edge */
1845    int width,				/* Width of entry */
1846    int height)				/* Height of entry */
1847{
1848    if ((mePtr->underline >= 0) && (mePtr->labelPtr != NULL)) {
1849	int len;
1850
1851	/* do the unicode call just to prevent overruns */
1852	Tcl_GetUnicodeFromObj(mePtr->labelPtr, &len);
1853	if (mePtr->underline < len) {
1854	    CONST char *label, *start, *end;
1855
1856	    label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
1857	    start = Tcl_UtfAtIndex(label, mePtr->underline);
1858	    end = Tcl_UtfNext(start);
1859	    Tk_UnderlineChars(menuPtr->display, d,
1860		    gc, tkfont, label, x + mePtr->indicatorSpace,
1861		    y + (height + fmPtr->ascent - fmPtr->descent) / 2,
1862		    (int) (start - label), (int) (end - label));
1863	}
1864    }
1865}
1866
1867/*
1868 *--------------------------------------------------------------
1869 *
1870 * MenuKeyBindProc --
1871 *
1872 *	This procedure is invoked when keys related to pulling
1873 *	down menus is pressed. The corresponding Windows events
1874 *	are generated and passed to DefWindowProc if appropriate.
1875 *
1876 * Results:
1877 *	Always returns TCL_OK.
1878 *
1879 * Side effects:
1880 *	The menu system may take over and process user events
1881 *	for menu input.
1882 *
1883 *--------------------------------------------------------------
1884 */
1885
1886static int
1887MenuKeyBindProc(clientData, interp, eventPtr, tkwin, keySym)
1888    ClientData clientData;	/* not used in this proc */
1889    Tcl_Interp *interp;		/* The interpreter of the receiving window. */
1890    XEvent *eventPtr;		/* The XEvent to process */
1891    Tk_Window tkwin;		/* The window receiving the event */
1892    KeySym keySym;		/* The key sym that is produced. */
1893{
1894    UINT scanCode;
1895    UINT virtualKey;
1896    TkWindow *winPtr = (TkWindow *)tkwin;
1897    int i;
1898
1899    if (eventPtr->type == KeyPress) {
1900	switch (keySym) {
1901	case XK_Alt_L:
1902	    scanCode = MapVirtualKey(VK_LMENU, 0);
1903	    CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1904		    WM_SYSKEYDOWN, VK_MENU, (scanCode << 16)
1905		    | (1 << 29));
1906	    break;
1907	case XK_Alt_R:
1908	    scanCode = MapVirtualKey(VK_RMENU, 0);
1909	    CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1910		    WM_SYSKEYDOWN, VK_MENU, (scanCode << 16)
1911		    | (1 << 29) | (1 << 24));
1912	    break;
1913	case XK_F10:
1914	    scanCode = MapVirtualKey(VK_F10, 0);
1915	    CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1916		    WM_SYSKEYDOWN, VK_F10, (scanCode << 16));
1917	    break;
1918	default:
1919	    virtualKey = XKeysymToKeycode(winPtr->display, keySym);
1920	    scanCode = MapVirtualKey(virtualKey, 0);
1921	    if (0 != scanCode) {
1922		CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1923			WM_SYSKEYDOWN, virtualKey, ((scanCode << 16)
1924			| (1 << 29)));
1925		if (eventPtr->xkey.nbytes > 0) {
1926		    for (i = 0; i < eventPtr->xkey.nbytes; i++) {
1927			CallWindowProc(DefWindowProc,
1928				Tk_GetHWND(Tk_WindowId(tkwin)),
1929				WM_SYSCHAR,
1930				eventPtr->xkey.trans_chars[i],
1931				((scanCode << 16) | (1 << 29)));
1932		    }
1933		}
1934	    }
1935	}
1936    } else if (eventPtr->type == KeyRelease) {
1937	switch (keySym) {
1938	case XK_Alt_L:
1939	    scanCode = MapVirtualKey(VK_LMENU, 0);
1940	    CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1941		    WM_SYSKEYUP, VK_MENU, (scanCode << 16)
1942		    | (1 << 29) | (1 << 30) | (1 << 31));
1943	    break;
1944	case XK_Alt_R:
1945	    scanCode = MapVirtualKey(VK_RMENU, 0);
1946	    CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1947		    WM_SYSKEYUP, VK_MENU, (scanCode << 16) | (1 << 24)
1948		    | (0x111 << 29) | (1 << 30) | (1 << 31));
1949	    break;
1950	case XK_F10:
1951	    scanCode = MapVirtualKey(VK_F10, 0);
1952	    CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1953		    WM_SYSKEYUP, VK_F10, (scanCode << 16)
1954		    | (1 << 30) | (1 << 31));
1955	    break;
1956	default:
1957	    virtualKey = XKeysymToKeycode(winPtr->display, keySym);
1958	    scanCode = MapVirtualKey(virtualKey, 0);
1959	    if (0 != scanCode) {
1960		CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1961			WM_SYSKEYUP, virtualKey, ((scanCode << 16)
1962			| (1 << 29) | (1 << 30) | (1 << 31)));
1963	    }
1964	}
1965    }
1966    return TCL_OK;
1967}
1968
1969/*
1970 *--------------------------------------------------------------
1971 *
1972 * TkpInitializeMenuBindings --
1973 *
1974 *	For every interp, initializes the bindings for Windows
1975 *	menus. Does nothing on Mac or XWindows.
1976 *
1977 * Results:
1978 *	None.
1979 *
1980 * Side effects:
1981 *	C-level bindings are setup for the interp which will
1982 *	handle Alt-key sequences for menus without beeping
1983 *	or interfering with user-defined Alt-key bindings.
1984 *
1985 *--------------------------------------------------------------
1986 */
1987
1988void
1989TkpInitializeMenuBindings(interp, bindingTable)
1990    Tcl_Interp *interp;		    /* The interpreter to set. */
1991    Tk_BindingTable bindingTable;   /* The table to add to. */
1992{
1993    Tk_Uid uid = Tk_GetUid("all");
1994
1995    /*
1996     * We need to set up the bindings for menubars. These have to
1997     * recreate windows events, so we need to have a C-level
1998     * binding for this. We have to generate the WM_SYSKEYDOWNS
1999     * and WM_SYSKEYUPs appropriately.
2000     */
2001
2002    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
2003	    "<Alt_L>", MenuKeyBindProc, NULL, NULL);
2004    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
2005	    "<KeyRelease-Alt_L>", MenuKeyBindProc, NULL, NULL);
2006    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
2007	    "<Alt_R>", MenuKeyBindProc, NULL, NULL);
2008    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
2009	    "<KeyRelease-Alt_R>", MenuKeyBindProc, NULL, NULL);
2010    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
2011	    "<Alt-KeyPress>", MenuKeyBindProc, NULL, NULL);
2012    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
2013	    "<Alt-KeyRelease>", MenuKeyBindProc, NULL, NULL);
2014    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
2015	    "<KeyPress-F10>", MenuKeyBindProc, NULL, NULL);
2016    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
2017	    "<KeyRelease-F10>", MenuKeyBindProc, NULL, NULL);
2018}
2019
2020/*
2021 *----------------------------------------------------------------------
2022 *
2023 * DrawMenuEntryLabel --
2024 *
2025 *	This procedure draws the label part of a menu.
2026 *
2027 * Results:
2028 *	None.
2029 *
2030 * Side effects:
2031 *	Commands are output to X to display the menu in its
2032 *	current mode.
2033 *
2034 *----------------------------------------------------------------------
2035 */
2036static void
2037DrawMenuEntryLabel(
2038    TkMenu *menuPtr,			/* The menu we are drawing */
2039    TkMenuEntry *mePtr,			/* The entry we are drawing */
2040    Drawable d,				/* What we are drawing into */
2041    GC gc,				/* The gc we are drawing into */
2042    Tk_Font tkfont,			/* The precalculated font */
2043    CONST Tk_FontMetrics *fmPtr,	/* The precalculated font metrics */
2044    int x,				/* left edge */
2045    int y,				/* right edge */
2046    int width,				/* width of entry */
2047    int height)				/* height of entry */
2048{
2049    int indicatorSpace =  mePtr->indicatorSpace;
2050    int activeBorderWidth;
2051    int leftEdge;
2052    int imageHeight, imageWidth;
2053    int textHeight, textWidth;
2054    int haveImage = 0, haveText = 0;
2055    int imageXOffset = 0, imageYOffset = 0;
2056    int textXOffset = 0, textYOffset = 0;
2057
2058    Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin,
2059	    menuPtr->activeBorderWidthPtr, &activeBorderWidth);
2060    leftEdge = x + indicatorSpace + activeBorderWidth;
2061
2062    /*
2063     * Work out what we will need to draw first.
2064     */
2065
2066    if (mePtr->image != NULL) {
2067    	Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight);
2068        haveImage = 1;
2069    } else if (mePtr->bitmapPtr != NULL) {
2070	Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
2071	Tk_SizeOfBitmap(menuPtr->display, bitmap, &imageWidth, &imageHeight);
2072	haveImage = 1;
2073    }
2074    if (!haveImage || (mePtr->compound != COMPOUND_NONE)) {
2075        if (mePtr->labelLength > 0) {
2076	    char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
2077	    textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength);
2078	    textHeight = fmPtr->linespace;
2079	    haveText = 1;
2080        }
2081    }
2082
2083    /*
2084     * Now work out what the relative positions are.
2085     */
2086
2087    if (haveImage && haveText) {
2088	int fullWidth = (imageWidth > textWidth ? imageWidth : textWidth);
2089	switch ((enum compound) mePtr->compound) {
2090	    case COMPOUND_TOP: {
2091		textXOffset = (fullWidth - textWidth)/2;
2092		textYOffset = imageHeight/2 + 2;
2093		imageXOffset = (fullWidth - imageWidth)/2;
2094		imageYOffset = -textHeight/2;
2095		break;
2096	    }
2097	    case COMPOUND_BOTTOM: {
2098		textXOffset = (fullWidth - textWidth)/2;
2099		textYOffset = -imageHeight/2;
2100		imageXOffset = (fullWidth - imageWidth)/2;
2101		imageYOffset = textHeight/2 + 2;
2102		break;
2103	    }
2104	    case COMPOUND_LEFT: {
2105		/*
2106		 * The standard image position on Windows is in the indicator
2107		 * space to the left of the entries, unless this entry is a
2108		 * radio|check button because then the indicator space will
2109		 * be used.
2110		 */
2111		textXOffset = imageWidth + 2;
2112		textYOffset = 0;
2113		imageXOffset = 0;
2114		imageYOffset = 0;
2115		if ((mePtr->type != CHECK_BUTTON_ENTRY)
2116			&& (mePtr->type != RADIO_BUTTON_ENTRY)) {
2117		    textXOffset -= indicatorSpace;
2118		    if (textXOffset < 0) {
2119			textXOffset = 0;
2120		    }
2121		    imageXOffset = -indicatorSpace;
2122		}
2123		break;
2124	    }
2125	    case COMPOUND_RIGHT: {
2126		textXOffset = 0;
2127		textYOffset = 0;
2128		imageXOffset = textWidth + 2;
2129		imageYOffset = 0;
2130		break;
2131	    }
2132	    case COMPOUND_CENTER: {
2133		textXOffset = (fullWidth - textWidth)/2;
2134		textYOffset = 0;
2135		imageXOffset = (fullWidth - imageWidth)/2;
2136		imageYOffset = 0;
2137		break;
2138	    }
2139	    case COMPOUND_NONE: {break;}
2140	}
2141    } else {
2142	textXOffset = 0;
2143	textYOffset = 0;
2144	imageXOffset = 0;
2145	imageYOffset = 0;
2146    }
2147
2148    /*
2149     * Draw label and/or bitmap or image for entry.
2150     */
2151
2152    if (mePtr->image != NULL) {
2153    	if ((mePtr->selectImage != NULL)
2154	    	&& (mePtr->entryFlags & ENTRY_SELECTED)) {
2155	    Tk_RedrawImage(mePtr->selectImage, 0, 0,
2156		    imageWidth, imageHeight, d, leftEdge + imageXOffset,
2157	            (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset));
2158    	} else {
2159	    Tk_RedrawImage(mePtr->image, 0, 0, imageWidth,
2160		    imageHeight, d, leftEdge + imageXOffset,
2161		    (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset));
2162    	}
2163    } else if (mePtr->bitmapPtr != NULL) {
2164	Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
2165    	XCopyPlane(menuPtr->display, bitmap, d,	gc, 0, 0,
2166		(unsigned) imageWidth, (unsigned) imageHeight,
2167		leftEdge + imageXOffset,
2168		(int) (y + (mePtr->height - imageHeight)/2 + imageYOffset), 1);
2169    }
2170    if ((mePtr->compound != COMPOUND_NONE) || !haveImage) {
2171    	if (mePtr->labelLength > 0) {
2172	    int baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
2173	    char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
2174	    if (TkWinGetPlatformTheme() == TK_THEME_WIN_CLASSIC) {
2175	        /* Win 95/98 systems draw disabled menu text with a
2176	         * 3D highlight, unless the menu item is highlighted */
2177	        if ((mePtr->state == ENTRY_DISABLED) &&
2178	                ((mePtr->entryFlags & ENTRY_PLATFORM_FLAG1) == 0)) {
2179	            COLORREF oldFgColor = gc->foreground;
2180		    gc->foreground = GetSysColor(COLOR_3DHILIGHT);
2181	            Tk_DrawChars(menuPtr->display, d, gc, tkfont, label,
2182		            mePtr->labelLength, leftEdge + textXOffset + 1,
2183		            baseline + textYOffset + 1);
2184		    gc->foreground = oldFgColor;
2185	        }
2186	    }
2187	    Tk_DrawChars(menuPtr->display, d, gc, tkfont, label,
2188		    mePtr->labelLength, leftEdge + textXOffset,
2189		    baseline + textYOffset);
2190	    DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr,
2191	            x + textXOffset, y + textYOffset,
2192		    width, height);
2193	}
2194    }
2195
2196    if (mePtr->state == ENTRY_DISABLED) {
2197	if (menuPtr->disabledFgPtr == NULL) {
2198	    XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y,
2199		    (unsigned) width, (unsigned) height);
2200	} else if ((mePtr->image != NULL)
2201		&& (menuPtr->disabledImageGC != None)) {
2202	    XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
2203		    leftEdge + imageXOffset,
2204		    (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset),
2205		    (unsigned) imageWidth, (unsigned) imageHeight);
2206	}
2207    }
2208}
2209
2210/*
2211 *--------------------------------------------------------------
2212 *
2213 * TkpComputeMenubarGeometry --
2214 *
2215 *	This procedure is invoked to recompute the size and
2216 *	layout of a menu that is a menubar clone.
2217 *
2218 * Results:
2219 *	None.
2220 *
2221 * Side effects:
2222 *	Fields of menu entries are changed to reflect their
2223 *	current positions, and the size of the menu window
2224 *	itself may be changed.
2225 *
2226 *--------------------------------------------------------------
2227 */
2228
2229void
2230TkpComputeMenubarGeometry(menuPtr)
2231    TkMenu *menuPtr;		/* Structure describing menu. */
2232{
2233    TkpComputeStandardMenuGeometry(menuPtr);
2234}
2235
2236/*
2237 *----------------------------------------------------------------------
2238 *
2239 * DrawTearoffEntry --
2240 *
2241 *	This procedure draws the background part of a menu.
2242 *
2243 * Results:
2244 *	None.
2245 *
2246 * Side effects:
2247 *	Commands are output to X to display the menu in its
2248 *	current mode.
2249 *
2250 *----------------------------------------------------------------------
2251 */
2252
2253void
2254DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
2255    TkMenu *menuPtr;			/* The menu we are drawing */
2256    TkMenuEntry *mePtr;			/* The entry we are drawing */
2257    Drawable d;				/* The drawable we are drawing into */
2258    GC gc;				/* The gc we are drawing with */
2259    Tk_Font tkfont;			/* The font we are drawing with */
2260    CONST Tk_FontMetrics *fmPtr;	/* The metrics we are drawing with */
2261    int x;
2262    int y;
2263    int width;
2264    int height;
2265{
2266    XPoint points[2];
2267    int segmentWidth, maxX;
2268    Tk_3DBorder border;
2269
2270    if (menuPtr->menuType != MASTER_MENU) {
2271	return;
2272    }
2273
2274    points[0].x = x;
2275    points[0].y = y + height/2;
2276    points[1].y = points[0].y;
2277    segmentWidth = 6;
2278    maxX  = width - 1;
2279    border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
2280
2281    while (points[0].x < maxX) {
2282	points[1].x = points[0].x + segmentWidth;
2283	if (points[1].x > maxX) {
2284	    points[1].x = maxX;
2285	}
2286	Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1,
2287		TK_RELIEF_RAISED);
2288	points[0].x += 2*segmentWidth;
2289    }
2290}
2291
2292/*
2293 *----------------------------------------------------------------------
2294 *
2295 * TkpConfigureMenuEntry --
2296 *
2297 *	Processes configurations for menu entries.
2298 *
2299 * Results:
2300 *	Returns standard TCL result. If TCL_ERROR is returned, then
2301 *	the interp's result contains an error message.
2302 *
2303 * Side effects:
2304 *	Configuration information get set for mePtr; old resources
2305 *	get freed, if any need it.
2306 *
2307 *----------------------------------------------------------------------
2308 */
2309
2310int
2311TkpConfigureMenuEntry(mePtr)
2312    register TkMenuEntry *mePtr;	/* Information about menu entry;  may
2313					 * or may not already have values for
2314					 * some fields. */
2315{
2316    TkMenu *menuPtr = mePtr->menuPtr;
2317
2318    if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
2319	menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
2320	Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
2321    }
2322    return TCL_OK;
2323}
2324
2325/*
2326 *----------------------------------------------------------------------
2327 *
2328 * TkpDrawMenuEntry --
2329 *
2330 *	Draws the given menu entry at the given coordinates with the
2331 *	given attributes.
2332 *
2333 * Results:
2334 *	None.
2335 *
2336 * Side effects:
2337 *	X Server commands are executed to display the menu entry.
2338 *
2339 *----------------------------------------------------------------------
2340 */
2341
2342void
2343TkpDrawMenuEntry(mePtr, d, tkfont, menuMetricsPtr, x, y, width, height,
2344	strictMotif, drawArrow)
2345    TkMenuEntry *mePtr;		    /* The entry to draw */
2346    Drawable d;			    /* What to draw into */
2347    Tk_Font tkfont;		    /* Precalculated font for menu */
2348    CONST Tk_FontMetrics *menuMetricsPtr;
2349				    /* Precalculated metrics for menu */
2350    int x;			    /* X-coordinate of topleft of entry */
2351    int y;			    /* Y-coordinate of topleft of entry */
2352    int width;			    /* Width of the entry rectangle */
2353    int height;			    /* Height of the current rectangle */
2354    int strictMotif;		    /* Boolean flag */
2355    int drawArrow;		    /* Whether or not to draw the cascade
2356				     * arrow for cascade items. Only applies
2357				     * to Windows. */
2358{
2359    GC gc, indicatorGC;
2360    TkMenu *menuPtr = mePtr->menuPtr;
2361    Tk_3DBorder bgBorder, activeBorder;
2362    CONST Tk_FontMetrics *fmPtr;
2363    Tk_FontMetrics entryMetrics;
2364    int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0;
2365    int adjustedY = y + padY;
2366    int adjustedHeight = height - 2 * padY;
2367
2368    /*
2369     * Choose the gc for drawing the foreground part of the entry.
2370     */
2371
2372    if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) {
2373	gc = mePtr->activeGC;
2374	if (gc == NULL) {
2375	    gc = menuPtr->activeGC;
2376	}
2377    } else {
2378    	TkMenuEntry *cascadeEntryPtr;
2379    	int parentDisabled = 0;
2380	char *name;
2381
2382    	for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
2383    		cascadeEntryPtr != NULL;
2384    		cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
2385	    name = Tcl_GetStringFromObj(cascadeEntryPtr->namePtr, NULL);
2386    	    if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) {
2387    	    	if (mePtr->state == ENTRY_DISABLED) {
2388    	    	    parentDisabled = 1;
2389    	    	}
2390    	    	break;
2391    	    }
2392    	}
2393
2394	if (((parentDisabled || (mePtr->state == ENTRY_DISABLED)))
2395		&& (menuPtr->disabledFgPtr != NULL)) {
2396	    gc = mePtr->disabledGC;
2397	    if (gc == NULL) {
2398		gc = menuPtr->disabledGC;
2399	    }
2400	} else {
2401	    gc = mePtr->textGC;
2402	    if (gc == NULL) {
2403		gc = menuPtr->textGC;
2404	    }
2405	}
2406    }
2407    indicatorGC = mePtr->indicatorGC;
2408    if (indicatorGC == NULL) {
2409	indicatorGC = menuPtr->indicatorGC;
2410    }
2411
2412    bgBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
2413	    (mePtr->borderPtr == NULL) ? menuPtr->borderPtr
2414	    : mePtr->borderPtr);
2415    if (strictMotif) {
2416	activeBorder = bgBorder;
2417    } else {
2418	activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
2419	    (mePtr->activeBorderPtr == NULL) ? menuPtr->activeBorderPtr
2420	    : mePtr->activeBorderPtr);
2421    }
2422
2423    if (mePtr->fontPtr == NULL) {
2424	fmPtr = menuMetricsPtr;
2425    } else {
2426	tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
2427	Tk_GetFontMetrics(tkfont, &entryMetrics);
2428	fmPtr = &entryMetrics;
2429    }
2430
2431    /*
2432     * Need to draw the entire background, including padding. On Unix,
2433     * for menubars, we have to draw the rest of the entry taking
2434     * into account the padding.
2435     */
2436
2437    DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder,
2438	    bgBorder, x, y, width, height);
2439
2440    if (mePtr->type == SEPARATOR_ENTRY) {
2441	DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont,
2442		fmPtr, x, adjustedY, width, adjustedHeight);
2443    } else if (mePtr->type == TEAROFF_ENTRY) {
2444	DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
2445		width, adjustedHeight);
2446    } else {
2447	DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
2448		width, adjustedHeight);
2449	DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
2450		activeBorder, x, adjustedY, width, adjustedHeight);
2451	DrawMenuEntryArrow(menuPtr, mePtr, d, gc,
2452		activeBorder, x, adjustedY, width, adjustedHeight, drawArrow);
2453	if (!mePtr->hideMargin) {
2454	    DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont,
2455		    fmPtr, x, adjustedY, width, adjustedHeight);
2456	}
2457    }
2458}
2459
2460/*
2461 *----------------------------------------------------------------------
2462 *
2463 * GetMenuLabelGeometry --
2464 *
2465 *	Figures out the size of the label portion of a menu item.
2466 *
2467 * Results:
2468 *	widthPtr and heightPtr are filled in with the correct geometry
2469 *	information.
2470 *
2471 * Side effects:
2472 *	None.
2473 *
2474 *----------------------------------------------------------------------
2475 */
2476
2477static void
2478GetMenuLabelGeometry(mePtr, tkfont, fmPtr, widthPtr, heightPtr)
2479    TkMenuEntry *mePtr;			/* The entry we are computing */
2480    Tk_Font tkfont;			/* The precalculated font */
2481    CONST Tk_FontMetrics *fmPtr;	/* The precalculated metrics */
2482    int *widthPtr;			/* The resulting width of the label
2483					 * portion */
2484    int *heightPtr;			/* The resulting height of the label
2485					 * portion */
2486{
2487    TkMenu *menuPtr = mePtr->menuPtr;
2488    int haveImage = 0, haveText = 0;
2489
2490    if (mePtr->image != NULL) {
2491    	Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr);
2492	haveImage = 1;
2493    } else if (mePtr->bitmapPtr != NULL) {
2494	Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
2495    	Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr);
2496	haveImage = 1;
2497    } else {
2498	*heightPtr = 0;
2499	*widthPtr = 0;
2500    }
2501
2502    if (haveImage && (mePtr->compound == COMPOUND_NONE)) {
2503	/* We don't care about the text in this case */
2504    } else {
2505	/* Either it is compound or we don't have an image */
2506    	if (mePtr->labelPtr != NULL) {
2507	    int textWidth;
2508	    char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
2509	    textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength);
2510
2511	    if ((mePtr->compound != COMPOUND_NONE) && haveImage) {
2512		switch ((enum compound) mePtr->compound) {
2513		    case COMPOUND_TOP:
2514		    case COMPOUND_BOTTOM: {
2515			if (textWidth > *widthPtr) {
2516			    *widthPtr = textWidth;
2517			}
2518			/* Add text and padding */
2519			*heightPtr += fmPtr->linespace + 2;
2520			break;
2521		    }
2522		    case COMPOUND_LEFT:
2523		    case COMPOUND_RIGHT: {
2524			if (fmPtr->linespace > *heightPtr) {
2525			    *heightPtr = fmPtr->linespace;
2526			}
2527			/* Add text and padding */
2528			*widthPtr += textWidth + 2;
2529			break;
2530		    }
2531		    case COMPOUND_CENTER: {
2532			if (fmPtr->linespace > *heightPtr) {
2533			    *heightPtr = fmPtr->linespace;
2534			}
2535			if (textWidth > *widthPtr) {
2536			    *widthPtr = textWidth;
2537			}
2538			break;
2539		    }
2540		    case COMPOUND_NONE: {break;}
2541		}
2542    	} else {
2543		/* We don't have an image or we're not compound */
2544		*heightPtr = fmPtr->linespace;
2545		*widthPtr = textWidth;
2546	    }
2547	} else {
2548	    /* An empty entry still has this height */
2549	    *heightPtr = fmPtr->linespace;
2550    	}
2551    }
2552    *heightPtr += 1;
2553}
2554
2555/*
2556 *----------------------------------------------------------------------
2557 *
2558 * DrawMenuEntryBackground --
2559 *
2560 *	This procedure draws the background part of a menu.
2561 *
2562 * Results:
2563 *	None.
2564 *
2565 * Side effects:
2566 *	Commands are output to X to display the menu in its
2567 *	current mode.
2568 *
2569 *----------------------------------------------------------------------
2570 */
2571
2572static void
2573DrawMenuEntryBackground(
2574    TkMenu *menuPtr,			/* The menu we are drawing. */
2575    TkMenuEntry *mePtr,			/* The entry we are drawing. */
2576    Drawable d,				/* What we are drawing into */
2577    Tk_3DBorder activeBorder,		/* Border for active items */
2578    Tk_3DBorder bgBorder,		/* Border for the background */
2579    int x,				/* left edge */
2580    int y,				/* top edge */
2581    int width,				/* width of rectangle to draw */
2582    int height)				/* height of rectangle to draw */
2583{
2584    if (mePtr->state == ENTRY_ACTIVE
2585		|| (mePtr->entryFlags & ENTRY_PLATFORM_FLAG1)!=0 ) {
2586	bgBorder = activeBorder;
2587    }
2588    Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder,
2589    	    x, y, width, height, 0, TK_RELIEF_FLAT);
2590}
2591
2592/*
2593 *--------------------------------------------------------------
2594 *
2595 * TkpComputeStandardMenuGeometry --
2596 *
2597 *	This procedure is invoked to recompute the size and
2598 *	layout of a menu that is not a menubar clone.
2599 *
2600 * Results:
2601 *	None.
2602 *
2603 * Side effects:
2604 *	Fields of menu entries are changed to reflect their
2605 *	current positions, and the size of the menu window
2606 *	itself may be changed.
2607 *
2608 *--------------------------------------------------------------
2609 */
2610
2611void
2612TkpComputeStandardMenuGeometry(
2613    TkMenu *menuPtr)		/* Structure describing menu. */
2614{
2615    Tk_Font menuFont, tkfont;
2616    Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
2617    int x, y, height, width, indicatorSpace, labelWidth, accelWidth;
2618    int windowWidth, windowHeight, accelSpace;
2619    int i, j, lastColumnBreak = 0;
2620    int activeBorderWidth, borderWidth;
2621
2622    if (menuPtr->tkwin == NULL) {
2623	return;
2624    }
2625
2626    Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin,
2627	    menuPtr->borderWidthPtr, &borderWidth);
2628    x = y = borderWidth;
2629    indicatorSpace = labelWidth = accelWidth = 0;
2630    windowHeight = 0;
2631
2632    /*
2633     * On the Mac especially, getting font metrics can be quite slow,
2634     * so we want to do it intelligently. We are going to precalculate
2635     * them and pass them down to all of the measuring and drawing
2636     * routines. We will measure the font metrics of the menu once.
2637     * If an entry does not have its own font set, then we give
2638     * the geometry/drawing routines the menu's font and metrics.
2639     * If an entry has its own font, we will measure that font and
2640     * give all of the geometry/drawing the entry's font and metrics.
2641     */
2642
2643    menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
2644    Tk_GetFontMetrics(menuFont, &menuMetrics);
2645    accelSpace = Tk_TextWidth(menuFont, "M", 1);
2646    Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin,
2647	    menuPtr->activeBorderWidthPtr, &activeBorderWidth);
2648
2649    for (i = 0; i < menuPtr->numEntries; i++) {
2650	if (menuPtr->entries[i]->fontPtr == NULL) {
2651	    tkfont = menuFont;
2652	    fmPtr = &menuMetrics;
2653	} else {
2654	    tkfont = Tk_GetFontFromObj(menuPtr->tkwin,
2655		    menuPtr->entries[i]->fontPtr);
2656    	    Tk_GetFontMetrics(tkfont, &entryMetrics);
2657    	    fmPtr = &entryMetrics;
2658    	}
2659	if ((i > 0) && menuPtr->entries[i]->columnBreak) {
2660	    if (accelWidth != 0) {
2661		labelWidth += accelSpace;
2662	    }
2663	    for (j = lastColumnBreak; j < i; j++) {
2664		menuPtr->entries[j]->indicatorSpace = indicatorSpace;
2665		menuPtr->entries[j]->labelWidth = labelWidth;
2666		menuPtr->entries[j]->width = indicatorSpace + labelWidth
2667			+ accelWidth + 2 * activeBorderWidth;
2668		menuPtr->entries[j]->x = x;
2669		menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN;
2670	    }
2671	    x += indicatorSpace + labelWidth + accelWidth
2672		    + 2 * borderWidth;
2673	    indicatorSpace = labelWidth = accelWidth = 0;
2674	    lastColumnBreak = i;
2675	    y = borderWidth;
2676	}
2677
2678	if (menuPtr->entries[i]->type == SEPARATOR_ENTRY) {
2679	    GetMenuSeparatorGeometry(menuPtr, menuPtr->entries[i], tkfont,
2680	    	    fmPtr, &width, &height);
2681	    menuPtr->entries[i]->height = height;
2682	} else if (menuPtr->entries[i]->type == TEAROFF_ENTRY) {
2683	    GetTearoffEntryGeometry(menuPtr, menuPtr->entries[i], tkfont,
2684	    	    fmPtr, &width, &height);
2685	    menuPtr->entries[i]->height = height;
2686	} else {
2687
2688	    /*
2689	     * For each entry, compute the height required by that
2690	     * particular entry, plus three widths:  the width of the
2691	     * label, the width to allow for an indicator to be displayed
2692	     * to the left of the label (if any), and the width of the
2693	     * accelerator to be displayed to the right of the label
2694	     * (if any).  These sizes depend, of course, on the type
2695	     * of the entry.
2696	     */
2697
2698	    GetMenuLabelGeometry(menuPtr->entries[i], tkfont, fmPtr, &width,
2699	    	    &height);
2700	    menuPtr->entries[i]->height = height;
2701	    if (width > labelWidth) {
2702	    	labelWidth = width;
2703	    }
2704
2705	    GetMenuAccelGeometry(menuPtr, menuPtr->entries[i], tkfont,
2706		    fmPtr, &width, &height);
2707	    if (height > menuPtr->entries[i]->height) {
2708	    	menuPtr->entries[i]->height = height;
2709	    }
2710	    if (width > accelWidth) {
2711	    	accelWidth = width;
2712	    }
2713
2714	    GetMenuIndicatorGeometry(menuPtr, menuPtr->entries[i], tkfont,
2715	    	    fmPtr, &width, &height);
2716	    if (height > menuPtr->entries[i]->height) {
2717	    	menuPtr->entries[i]->height = height;
2718	    }
2719	    if (width > indicatorSpace) {
2720	    	indicatorSpace = width;
2721	    }
2722
2723	    menuPtr->entries[i]->height += 2 * activeBorderWidth + 1;
2724    	}
2725        menuPtr->entries[i]->y = y;
2726	y += menuPtr->entries[i]->height;
2727	if (y > windowHeight) {
2728	    windowHeight = y;
2729	}
2730    }
2731
2732    if (accelWidth != 0) {
2733	labelWidth += accelSpace;
2734    }
2735    for (j = lastColumnBreak; j < menuPtr->numEntries; j++) {
2736	menuPtr->entries[j]->indicatorSpace = indicatorSpace;
2737	menuPtr->entries[j]->labelWidth = labelWidth;
2738	menuPtr->entries[j]->width = indicatorSpace + labelWidth
2739		+ accelWidth + 2 * activeBorderWidth;
2740	menuPtr->entries[j]->x = x;
2741	menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN;
2742    }
2743    windowWidth = x + indicatorSpace + labelWidth + accelWidth + accelSpace
2744	    + 2 * activeBorderWidth + 2 * borderWidth;
2745
2746
2747    windowHeight += borderWidth;
2748
2749    /*
2750     * The X server doesn't like zero dimensions, so round up to at least
2751     * 1 (a zero-sized menu should never really occur, anyway).
2752     */
2753
2754    if (windowWidth <= 0) {
2755	windowWidth = 1;
2756    }
2757    if (windowHeight <= 0) {
2758	windowHeight = 1;
2759    }
2760    menuPtr->totalWidth = windowWidth;
2761    menuPtr->totalHeight = windowHeight;
2762}
2763
2764/*
2765 *----------------------------------------------------------------------
2766 *
2767 * MenuSelectEvent --
2768 *
2769 *	Generates a "MenuSelect" virtual event. This can be used to
2770 *	do context-sensitive menu help.
2771 *
2772 * Results:
2773 *	None.
2774 *
2775 * Side effects:
2776 *	Places a virtual event on the event queue.
2777 *
2778 *----------------------------------------------------------------------
2779 */
2780
2781static void
2782MenuSelectEvent(
2783    TkMenu *menuPtr)		/* the menu we have selected. */
2784{
2785    XVirtualEvent event;
2786    POINTS rootPoint;
2787    DWORD msgPos;
2788
2789    event.type = VirtualEvent;
2790    event.serial = menuPtr->display->request;
2791    event.send_event = 0;
2792    event.display = menuPtr->display;
2793    Tk_MakeWindowExist(menuPtr->tkwin);
2794    event.event = Tk_WindowId(menuPtr->tkwin);
2795    event.root = XRootWindow(menuPtr->display, 0);
2796    event.subwindow = None;
2797    event.time = TkpGetMS();
2798
2799    msgPos = GetMessagePos();
2800    rootPoint = MAKEPOINTS(msgPos);
2801    event.x_root = rootPoint.x;
2802    event.y_root = rootPoint.y;
2803    event.state = TkWinGetModifierState();
2804    event.same_screen = 1;
2805    event.name = Tk_GetUid("MenuSelect");
2806    Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL);
2807}
2808
2809/*
2810 *----------------------------------------------------------------------
2811 *
2812 * TkpMenuNotifyToplevelCreate --
2813 *
2814 *	This routine reconfigures the menu and the clones indicated by
2815 *	menuName becuase a toplevel has been created and any system
2816 *	menus need to be created.
2817 *
2818 * Results:
2819 *	None.
2820 *
2821 * Side effects:
2822 *	An idle handler is set up to do the reconfiguration.
2823 *
2824 *----------------------------------------------------------------------
2825 */
2826
2827void
2828TkpMenuNotifyToplevelCreate(
2829    Tcl_Interp *interp,			/* The interp the menu lives in. */
2830    char *menuName)			/* The name of the menu to
2831					 * reconfigure. */
2832{
2833    TkMenuReferences *menuRefPtr;
2834    TkMenu *menuPtr;
2835
2836    if ((menuName != NULL) && (menuName[0] != '\0')) {
2837	menuRefPtr = TkFindMenuReferences(interp, menuName);
2838	if ((menuRefPtr != NULL) && (menuRefPtr->menuPtr != NULL)) {
2839	    for (menuPtr = menuRefPtr->menuPtr->masterMenuPtr; menuPtr != NULL;
2840		    menuPtr = menuPtr->nextInstancePtr) {
2841		if ((menuPtr->menuType == MENUBAR)
2842			&& !(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
2843		    menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
2844		    Tcl_DoWhenIdle(ReconfigureWindowsMenu,
2845			    (ClientData) menuPtr);
2846		}
2847	    }
2848	}
2849    }
2850}
2851
2852/*
2853 *----------------------------------------------------------------------
2854 *
2855 * MenuExitHandler --
2856 *
2857 *	Unregisters the class of utility windows.
2858 *
2859 * Results:
2860 *	None.
2861 *
2862 * Side effects:
2863 *	Menus have to be reinitialized next time.
2864 *
2865 *----------------------------------------------------------------------
2866 */
2867
2868static void
2869MenuExitHandler(
2870    ClientData clientData)	    /* Not used */
2871{
2872    UnregisterClass(MENU_CLASS_NAME, Tk_GetHINSTANCE());
2873}
2874
2875/*
2876 *----------------------------------------------------------------------
2877 *
2878 * MenuExitHandler --
2879 *
2880 *	Throws away the utility window needed for menus and delete hash
2881 *	tables.
2882 *
2883 * Results:
2884 *	None.
2885 *
2886 * Side effects:
2887 *	Menus have to be reinitialized next time.
2888 *
2889 *----------------------------------------------------------------------
2890 */
2891
2892static void
2893MenuThreadExitHandler(
2894    ClientData clientData)	    /* Not used */
2895{
2896    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
2897	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
2898
2899    DestroyWindow(tsdPtr->menuHWND);
2900    tsdPtr->menuHWND = NULL;
2901
2902    Tcl_DeleteHashTable(&tsdPtr->winMenuTable);
2903    Tcl_DeleteHashTable(&tsdPtr->commandTable);
2904}
2905
2906/*
2907 *----------------------------------------------------------------------
2908 *
2909 * TkWinGetMenuSystemDefault --
2910 *
2911 *	Gets the Windows specific default value for a given X resource
2912 *	database name.
2913 *
2914 * Results:
2915 *	Returns a Tcl_Obj * with the default value. If there is no
2916 *	Windows-specific default for this attribute, returns NULL.
2917 *	This object has a ref count of 0.
2918 *
2919 * Side effects:
2920 *	Storage is allocated.
2921 *
2922 *----------------------------------------------------------------------
2923 */
2924
2925Tcl_Obj *
2926TkWinGetMenuSystemDefault(
2927    Tk_Window tkwin,		/* A window to use. */
2928    CONST char *dbName,		/* The option database name. */
2929    CONST char *className)	/* The name of the option class. */
2930{
2931    Tcl_Obj *valuePtr = NULL;
2932
2933    if ((strcmp(dbName, "activeBorderWidth") == 0) ||
2934	    (strcmp(dbName, "borderWidth") == 0)) {
2935	valuePtr = Tcl_NewIntObj(defaultBorderWidth);
2936    } else if (strcmp(dbName, "font") == 0) {
2937	valuePtr = Tcl_NewStringObj(Tcl_DStringValue(&menuFontDString),
2938		-1);
2939    }
2940
2941    return valuePtr;
2942}
2943
2944/*
2945 *----------------------------------------------------------------------
2946 *
2947 * TkWinMenuSetDefaults --
2948 *
2949 *	Sets up the hash tables and the variables used by the menu package.
2950 *
2951 * Results:
2952 *	None.
2953 *
2954 * Side effects:
2955 *	lastMenuID gets initialized, and the parent hash and the command hash
2956 *	are allocated.
2957 *
2958 *----------------------------------------------------------------------
2959 */
2960
2961void
2962SetDefaults(
2963    int firstTime)		    /* Is this the first time this
2964				     * has been called? */
2965{
2966    char sizeString[TCL_INTEGER_SPACE];
2967    char faceName[LF_FACESIZE];
2968    HDC scratchDC;
2969    Tcl_DString boldItalicDString;
2970    int bold = 0;
2971    int italic = 0;
2972    TEXTMETRIC tm;
2973    int pointSize;
2974    HFONT menuFont;
2975    NONCLIENTMETRICS ncMetrics;
2976
2977    /*
2978     * Set all of the default options. The loop will terminate when we run
2979     * out of options via a break statement.
2980     */
2981
2982    defaultBorderWidth = GetSystemMetrics(SM_CXBORDER);
2983    if (GetSystemMetrics(SM_CYBORDER) > defaultBorderWidth) {
2984	defaultBorderWidth = GetSystemMetrics(SM_CYBORDER);
2985    }
2986
2987    scratchDC = CreateDC("DISPLAY", NULL, NULL, NULL);
2988    if (!firstTime) {
2989	Tcl_DStringFree(&menuFontDString);
2990    }
2991    Tcl_DStringInit(&menuFontDString);
2992
2993    ncMetrics.cbSize = sizeof(ncMetrics);
2994    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncMetrics),
2995	    &ncMetrics, 0);
2996    menuFont = CreateFontIndirect(&ncMetrics.lfMenuFont);
2997    SelectObject(scratchDC, menuFont);
2998    GetTextMetrics(scratchDC, &tm);
2999    GetTextFace(scratchDC, LF_FACESIZE, faceName);
3000    pointSize = MulDiv(tm.tmHeight - tm.tmInternalLeading,
3001	    72, GetDeviceCaps(scratchDC, LOGPIXELSY));
3002    if (tm.tmWeight >= 700) {
3003	bold = 1;
3004    }
3005    if (tm.tmItalic) {
3006	italic = 1;
3007    }
3008
3009    SelectObject(scratchDC, GetStockObject(SYSTEM_FONT));
3010    DeleteDC(scratchDC);
3011
3012    DeleteObject(menuFont);
3013
3014    Tcl_DStringAppendElement(&menuFontDString, faceName);
3015    sprintf(sizeString, "%d", pointSize);
3016    Tcl_DStringAppendElement(&menuFontDString, sizeString);
3017
3018    if (bold == 1 || italic == 1) {
3019	Tcl_DStringInit(&boldItalicDString);
3020	if (bold == 1) {
3021	    Tcl_DStringAppendElement(&boldItalicDString, "bold");
3022	}
3023	if (italic == 1) {
3024	    Tcl_DStringAppendElement(&boldItalicDString, "italic");
3025	}
3026	Tcl_DStringAppendElement(&menuFontDString,
3027		Tcl_DStringValue(&boldItalicDString));
3028    }
3029
3030    /*
3031     * Now we go ahead and get the dimensions of the check mark and the
3032     * appropriate margins. Since this is fairly hairy, we do it here
3033     * to save time when traversing large sets of menu items.
3034     *
3035     * The code below was given to me by Microsoft over the phone. It
3036     * is the only way to insure menu items lining up, and is not
3037     * documented.
3038     */
3039
3040    if (TkWinGetPlatformId() >= VER_PLATFORM_WIN32_WINDOWS) {
3041	indicatorDimensions[0] = GetSystemMetrics(SM_CYMENUCHECK);
3042	indicatorDimensions[1] = ((GetSystemMetrics(SM_CXFIXEDFRAME) +
3043		GetSystemMetrics(SM_CXBORDER)
3044		+ GetSystemMetrics(SM_CXMENUCHECK) + 7) & 0xFFF8)
3045		- GetSystemMetrics(SM_CXFIXEDFRAME);
3046    } else {
3047	DWORD dimensions = GetMenuCheckMarkDimensions();
3048	indicatorDimensions[0] = HIWORD(dimensions);
3049	indicatorDimensions[1] = LOWORD(dimensions);
3050   }
3051}
3052
3053/*
3054 *----------------------------------------------------------------------
3055 *
3056 * TkpMenuInit --
3057 *
3058 *	Sets up the process-wide variables used by the menu package.
3059 *
3060 * Results:
3061 *	None.
3062 *
3063 * Side effects:
3064 *	lastMenuID gets initialized.
3065 *
3066 *----------------------------------------------------------------------
3067 */
3068
3069void
3070TkpMenuInit()
3071{
3072    WNDCLASS wndClass;
3073
3074    wndClass.style = CS_OWNDC;
3075    wndClass.lpfnWndProc = TkWinMenuProc;
3076    wndClass.cbClsExtra = 0;
3077    wndClass.cbWndExtra = 0;
3078    wndClass.hInstance = Tk_GetHINSTANCE();
3079    wndClass.hIcon = NULL;
3080    wndClass.hCursor = NULL;
3081    wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
3082    wndClass.lpszMenuName = NULL;
3083    wndClass.lpszClassName = MENU_CLASS_NAME;
3084    RegisterClass(&wndClass);
3085
3086    TkCreateExitHandler(MenuExitHandler, (ClientData) NULL);
3087    SetDefaults(1);
3088}
3089
3090/*
3091 *----------------------------------------------------------------------
3092 *
3093 * TkpMenuThreadInit --
3094 *
3095 *	Sets up the thread-local hash tables used by the menu module.
3096 *
3097 * Results:
3098 *	None.
3099 *
3100 * Side effects:
3101 *	Hash tables winMenuTable and commandTable are initialized.
3102 *
3103 *----------------------------------------------------------------------
3104 */
3105
3106void
3107TkpMenuThreadInit()
3108{
3109    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
3110            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
3111
3112    tsdPtr->menuHWND = CreateWindow(MENU_CLASS_NAME, "MenuWindow", WS_POPUP,
3113	0, 0, 10, 10, NULL, NULL, Tk_GetHINSTANCE(), NULL);
3114
3115    Tcl_InitHashTable(&tsdPtr->winMenuTable, TCL_ONE_WORD_KEYS);
3116    Tcl_InitHashTable(&tsdPtr->commandTable, TCL_ONE_WORD_KEYS);
3117
3118    Tcl_CreateThreadExitHandler(MenuThreadExitHandler, (ClientData) NULL);
3119}
3120