/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkwinmenu.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkwinmenu.c

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25