OSDN Git Service

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