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