home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / win / tkWinMenu.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  73.1 KB  |  2,645 lines  |  [TEXT/CWIE]

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