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