tkWinMenu.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:87k
源码类别:

通讯编程

开发平台:

Visual C++

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