/[dtapublic]/projs/ets/trunk/src/c_tk_base_7_5_w_mods/tkwinmenu.c
ViewVC logotype

Diff of /projs/ets/trunk/src/c_tk_base_7_5_w_mods/tkwinmenu.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.44  
changed lines
  Added in v.220

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25