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

Contents of /projs/dtats/trunk/shared_source/c_tk_base_7_5_w_mods/tkwinmenu.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 98 - (show annotations) (download)
Sun Dec 18 00:57:31 2016 UTC (7 years, 11 months ago) by dashley
File MIME type: text/plain
File size: 80124 byte(s)
Reorganization.
1 /* $Header$ */
2
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 /* End of tkwinmenu.c */

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25