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