XEmacs 21.2.36 "Notos"
[chise/xemacs-chise.git.1] / src / menubar-msw.c
index e3db02f..5e0625a 100644 (file)
@@ -1,7 +1,8 @@
 /* Implements an elisp-programmable menubar -- Win32
    Copyright (C) 1993, 1994 Free Software Foundation, Inc.
    Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
-   Copyright (C) 1997 Kirill M. Katsnelson <kkm@kis.ru>
+   Copyright (C) 1997 Kirill M. Katsnelson <kkm@kis.ru>.
+   Copyright (C) 2000 Ben Wing.
 
 This file is part of XEmacs.
 
@@ -70,14 +71,13 @@ Boston, MA 02111-1307, USA.  */
  * particular knowledge is bad because this may break in Windows NT
  * 5.0, or Windows 98, or other future version. Instead, I allow the
  * hash tables to hang around, and not clear them, unless WM_COMMAND is
- * received. This is worthy some memory but more safe. Hacks welcome,
+ * received. This is worth some memory but more safe. Hacks welcome,
  * anyways!
  *
  */
 
 #include <config.h>
 #include "lisp.h"
-#include <limits.h>
 
 #include "buffer.h"
 #include "commands.h"
@@ -93,7 +93,7 @@ Boston, MA 02111-1307, USA.  */
 #include "window.h"
 
 /* #### */
-#define REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIHGT_FLUSH 0
+#define REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIGHT_FLUSH 0
 
 #define EMPTY_ITEM_ID ((UINT)LISP_TO_VOID (Qunbound))
 #define EMPTY_ITEM_NAME "(empty)"
@@ -116,54 +116,129 @@ static Lisp_Object current_hash_table;
 #define MENU_ITEM_ID_BITS(x) (((x) & 0x7FFF) | 0x8000)
 static HMENU top_level_menu;
 
-#define MAX_MENUITEM_LENGTH 128
+/*
+ * Translate (in place) X accelerator syntax to win32 accelerator syntax.
+ * Return new length.
+ * len = number of bytes (not including zero terminator).
+ * maxlen = size of buffer.
+ * accel = (Emchar*) to receive the accelerator character
+ *         or NULL to suppress accelerators in the menu or dialog item.
+ *
+ * %% is replaced with %
+ * if accel is NULL:
+ *   %_ is removed.
+ * if accel is non-NULL:
+ *   %_ is replaced with &.
+ *   The accelerator character is passed back in *accel.
+ *   (If there is no accelerator, it will be added on the first character.)
+ *
+ * We assume and maintain zero-termination.  To be absolutely sure
+ * of not hitting an error, maxlen should be >= 2*len + 3.
+ */
+Bytecount
+mswindows_translate_menu_or_dialog_item (Bufbyte *item, Bytecount len,
+                                        Bytecount maxlen, Emchar *accel,
+                                        Lisp_Object error_name)
+{
+  Bufbyte *ptr;
+
+  if (accel)
+    *accel = '\0';
+
+  /* Escape '&' as '&&' */
+  ptr = item;
+  while ((ptr = (Bufbyte *) memchr (ptr, '&', len - (ptr - item))) != NULL)
+    {
+      if (len + 2 > maxlen)
+       syntax_error ("Menu item produces too long displayable string",
+                     error_name);
+      memmove (ptr + 1, ptr, (len - (ptr - item)) + 1);
+      len++;
+      ptr += 2;
+    }
+
+  /* Replace XEmacs accelerator '%_' with Windows accelerator '&'
+     and `%%' with `%'. */
+  ptr = item;
+  while ((ptr = memchr (ptr, '%', len - (ptr - item))) != NULL)
+    {
+      if (*(ptr + 1) == '_')
+       {
+         if (accel)
+           {
+             *ptr = '&';
+             if (!*accel)
+               /* #### urk !  We need a reference translation table for
+                  case changes that aren't buffer-specific. */
+               *accel = DOWNCASE (current_buffer, charptr_emchar (ptr + 2));
+             memmove (ptr + 1, ptr + 2, len - (ptr - item + 2) + 1);
+             len--;
+           }
+         else  /* Skip accelerator */
+           {
+             memmove (ptr, ptr + 2, len - (ptr - item + 2) + 1);
+             len-=2;
+           }
+       }
+      else if (*(ptr + 1) == '%')
+       {
+         memmove (ptr + 1, ptr + 2, len - (ptr - item + 2) + 1);
+         len--;
+         ptr++;
+       }
+      else     /* % on its own - shouldn't happen */
+       ptr++;
+    }
+
+  if (accel && !*accel)
+    {
+      /* Force a default accelerator */
+      if (len + 2 > maxlen)
+       syntax_error ("Menu item produces too long displayable string",
+                     error_name);
+      ptr = item;
+      memmove (ptr + 1, ptr, len + 1);
+      /* #### urk !  We need a reference translation table for
+        case changes that aren't buffer-specific. */
+      *accel = DOWNCASE (current_buffer, charptr_emchar (ptr + 1));
+      *ptr = '&';
+
+      len++;
+    }
+
+  return len;
+}
 
 /*
  * This returns Windows-style menu item string:
  * "Left Flush\tRight Flush"
  */
+
+/* #### This is junk.  Need correct handling of sizes.  Use a Bufbyte_dynarr,
+   not a static buffer. */
 static char*
-displayable_menu_item (Lisp_Object gui_item, int bar_p)
+displayable_menu_item (Lisp_Object gui_item, int bar_p, Emchar *accel)
 {
+  unsigned int ll;
+
   /* We construct the name in a static buffer. That's fine, because
      menu items longer than 128 chars are probably programming errors,
      and better be caught than displayed! */
-  
+
   static char buf[MAX_MENUITEM_LENGTH+2];
-  char *ptr;
-  unsigned int ll, lr;
 
   /* Left flush part of the string */
   ll = gui_item_display_flush_left (gui_item, buf, MAX_MENUITEM_LENGTH);
 
-  /* Escape '&' as '&&' */
-  ptr = buf;
-  while ((ptr=memchr (ptr, '&', ll-(ptr-buf))) != NULL)
-    {
-      if (ll+2 >= MAX_MENUITEM_LENGTH)
-       signal_simple_error ("Menu item produces too long displayable string",
-                            XGUI_ITEM (gui_item)->name);
-      memmove (ptr+1, ptr, (ll-(ptr-buf))+1);
-      ll++;
-      ptr+=2;
-    }
-
-  /* Replace XEmacs accelerator '%_' with Windows accelerator '&' */
-  ptr = buf;
-  while ((ptr=memchr (ptr, '%', ll-(ptr-buf))) != NULL)
-    {
-      if (*(ptr+1) == '_')
-       {
-         *ptr = '&';
-         memmove (ptr+1, ptr+2, ll-(ptr-buf+2));
-         ll--;
-       }
-      ptr++;
-    }
+  ll = mswindows_translate_menu_or_dialog_item ((Bufbyte *) buf, ll,
+                                         MAX_MENUITEM_LENGTH, accel,
+                                         XGUI_ITEM (gui_item)->name);
 
   /* Right flush part, unless we're at the top-level where it's not allowed */
   if (!bar_p)
     {
+      unsigned int lr;
+
       assert (MAX_MENUITEM_LENGTH > ll + 1);
       lr = gui_item_display_flush_right (gui_item, buf + ll + 1,
                                         MAX_MENUITEM_LENGTH - ll - 1);
@@ -245,7 +320,7 @@ checksum_menu_item (Lisp_Object item)
       return HASH2 (internal_hash (XVECTOR_DATA(item)[0], 0),
                    internal_hash (XVECTOR_DATA(item)[1], 0));
     }
+
   /* An error - will be caught later */
   return 0;
 }
@@ -253,9 +328,13 @@ checksum_menu_item (Lisp_Object item)
 static void
 populate_menu_add_item (HMENU menu, Lisp_Object path,
                        Lisp_Object hash_tab, Lisp_Object item,
+                       Lisp_Object *accel_list,
                        int flush_right, int bar_p)
 {
   MENUITEMINFO item_info;
+  UINT oldflags = MF_BYPOSITION;
+  UINT olduidnewitem = 0;
+  LPCTSTR oldlpnewitem = 0;
 
   item_info.cbSize = sizeof (item_info);
   item_info.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
@@ -267,12 +346,17 @@ populate_menu_add_item (HMENU menu, Lisp_Object path,
     {
       /* Separator or unselectable text */
       if (separator_string_p (XSTRING_DATA (item)))
-       item_info.fType = MFT_SEPARATOR;
+       {
+         item_info.fType = MFT_SEPARATOR;
+         oldflags |= MF_SEPARATOR;
+       }
       else
        {
          item_info.fType = MFT_STRING;
          item_info.fState = MFS_DISABLED;
          item_info.dwTypeData = XSTRING_DATA (item);
+         oldflags |= MF_STRING | MF_DISABLED;
+         oldlpnewitem = item_info.dwTypeData;
        }
     }
   else if (CONSP (item))
@@ -280,66 +364,87 @@ populate_menu_add_item (HMENU menu, Lisp_Object path,
       /* Submenu */
       HMENU submenu;
       Lisp_Object gui_item = allocate_gui_item ();
-      struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
-      struct gcpro gcpro1;
+      Lisp_Gui_Item *pgui_item = XGUI_ITEM (gui_item);
+      struct gcpro gcpro1, gcpro2, gcpro3;
+      Emchar accel;
 
-      GCPRO1 (gui_item);
+      GCPRO3 (gui_item, path, *accel_list);
 
       menu_parse_submenu_keywords (item, gui_item);
 
       if (!STRINGP (pgui_item->name))
-       signal_simple_error ("Menu name (first element) must be a string", item);
+       syntax_error ("Menu name (first element) must be a string",
+                            item);
 
       if (!gui_item_included_p (gui_item, Vmenubar_configuration))
-       return;
+       {
+         UNGCPRO;
+         goto done;
+       }
 
       if (!gui_item_active_p (gui_item))
-       item_info.fState = MFS_GRAYED;
+       {
+         item_info.fState = MFS_GRAYED;
+         oldflags |= MF_GRAYED;
+       }
       /* Temptation is to put 'else' right here. Although, the
         displayed item won't have an arrow indicating that it is a
         popup.  So we go ahead a little bit more and create a popup */
-      submenu = create_empty_popup_menu();
+      submenu = create_empty_popup_menu ();
 
       item_info.fMask |= MIIM_SUBMENU;
-      item_info.dwTypeData = displayable_menu_item (gui_item, bar_p);
+      item_info.dwTypeData = displayable_menu_item (gui_item, bar_p, &accel);
       item_info.hSubMenu = submenu;
+      olduidnewitem = (UINT) submenu;
+      oldlpnewitem = item_info.dwTypeData;
+      oldflags |= MF_POPUP;
+
+      if (accel && bar_p)
+       *accel_list = Fcons (make_char (accel), *accel_list);
 
       if (!(item_info.fState & MFS_GRAYED))
        {
          /* Now add the full submenu path as a value to the hash table,
             keyed by menu handle */
          if (NILP(path))
-           /* list1 cannot GC */
            path = list1 (pgui_item->name);
          else
            {
              Lisp_Object arg[2];
              arg[0] = path;
              arg[1] = list1 (pgui_item->name);
-             /* Fappend gcpro'es its arg */
              path = Fappend (2, arg);
            }
 
-         /* Fputhash GCPRO'es PATH */
          Fputhash (hmenu_to_lisp_object (submenu), path, hash_tab);
        }
-      UNGCPRO; /* gui_item */
-    } 
+      UNGCPRO;
+    }
   else if (VECTORP (item))
     {
       /* An ordinary item */
       Lisp_Object style, id;
       Lisp_Object gui_item = gui_parse_item_keywords (item);
-      struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
-      struct gcpro gcpro1;
+      Lisp_Gui_Item *pgui_item = XGUI_ITEM (gui_item);
+      struct gcpro gcpro1, gcpro2;
+      Emchar accel;
 
-      GCPRO1 (gui_item);
+      GCPRO2 (gui_item, *accel_list);
 
       if (!gui_item_included_p (gui_item, Vmenubar_configuration))
-       return;
+       {
+         UNGCPRO;
+         goto done;
+       }
+
+      if (!STRINGP (pgui_item->name))
+       pgui_item->name = Feval (pgui_item->name);
 
       if (!gui_item_active_p (gui_item))
-       item_info.fState = MFS_GRAYED;
+       {
+         item_info.fState = MFS_GRAYED;
+         oldflags = MF_GRAYED;
+       }
 
       style = (NILP (pgui_item->selected) || NILP (Feval (pgui_item->selected))
               ? Qnil : pgui_item->style);
@@ -348,32 +453,44 @@ populate_menu_add_item (HMENU menu, Lisp_Object path,
        {
          item_info.fType |= MFT_RADIOCHECK;
          item_info.fState |= MFS_CHECKED;
+         oldflags |= MF_CHECKED; /* Can't support radio-button checkmarks
+                                    under 3.51 */
        }
       else if (EQ (style, Qtoggle))
        {
          item_info.fState |= MFS_CHECKED;
+         oldflags |= MF_CHECKED;
        }
 
       id = allocate_menu_item_id (path, pgui_item->name,
                                  pgui_item->suffix);
       Fputhash (id, pgui_item->callback, hash_tab);
 
-      item_info.wID = (UINT) XINT(id);
+      item_info.wID = (UINT) XINT (id);
       item_info.fType |= MFT_STRING;
-      item_info.dwTypeData = displayable_menu_item (gui_item, bar_p);
+      item_info.dwTypeData = displayable_menu_item (gui_item, bar_p, &accel);
+      olduidnewitem = item_info.wID;
+      oldflags |= MF_STRING;
+      oldlpnewitem = item_info.dwTypeData;
+
+      if (accel && bar_p)
+       *accel_list = Fcons (make_char (accel), *accel_list);
 
-      UNGCPRO; /* gui_item */
+      UNGCPRO;
     }
   else
-    {
-      signal_simple_error ("Malformed menu item descriptor", item);
-    }
+    syntax_error ("Malformed menu item descriptor", item);
 
   if (flush_right)
-    item_info.fType |= MFT_RIGHTJUSTIFY;
+    item_info.fType |= MFT_RIGHTJUSTIFY; /* can't support in 3.51 */
 
-  InsertMenuItem (menu, UINT_MAX, TRUE, &item_info);
-}  
+  if (xInsertMenuItemA)
+    xInsertMenuItemA (menu, UINT_MAX, TRUE, &item_info);
+  else
+    InsertMenu (menu, UINT_MAX, oldflags, olduidnewitem, oldlpnewitem);
+
+done:;
+}
 
 /*
  * This function is called from populate_menu and checksum_menu.
@@ -392,16 +509,18 @@ populate_or_checksum_helper (HMENU menu, Lisp_Object path, Lisp_Object desc,
 {
   Lisp_Object item_desc;
   int deep_p, flush_right;
-  struct gcpro gcpro1;
+  struct gcpro gcpro1, gcpro2, gcpro3;
   unsigned long checksum;
   Lisp_Object gui_item = allocate_gui_item ();
-  struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
-  GCPRO1 (gui_item);
+  Lisp_Object accel_list = Qnil;
+  Lisp_Gui_Item *pgui_item = XGUI_ITEM (gui_item);
+
+  GCPRO3 (gui_item, accel_list, desc);
 
   /* We are sometimes called with the menubar unchanged, and with changed
      right flush. We have to update the menubar in this case,
      so account for the compliance setting in the hash value */
-  checksum = REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIHGT_FLUSH;
+  checksum = REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIGHT_FLUSH;
 
   /* Will initially contain only "(empty)" */
   if (populate_p)
@@ -415,7 +534,7 @@ populate_or_checksum_helper (HMENU menu, Lisp_Object path, Lisp_Object desc,
 
   /* Check that menu name is specified when expected */
   if (NILP (pgui_item->name) && deep_p)
-    signal_simple_error ("Menu must have a name", desc);
+    syntax_error ("Menu must have a name", desc);
 
   /* Apply filter if specified */
   if (!NILP (pgui_item->filter))
@@ -428,19 +547,20 @@ populate_or_checksum_helper (HMENU menu, Lisp_Object path, Lisp_Object desc,
       if (NILP (XCAR (item_desc)))
        {
          /* Do not flush right menubar items when MS style compliant */
-         if (bar_p && !REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIHGT_FLUSH)
+         if (bar_p && !REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIGHT_FLUSH)
            flush_right = 1;
          if (!populate_p)
            checksum = HASH2 (checksum, LISP_HASH (Qnil));
        }
       else if (populate_p)
        populate_menu_add_item (menu, path, hash_tab,
-                               XCAR (item_desc), flush_right, bar_p);
+                               XCAR (item_desc), &accel_list,
+                               flush_right, bar_p);
       else
        checksum = HASH2 (checksum,
                          checksum_menu_item (XCAR (item_desc)));
     }
-  
+
   if (populate_p)
     {
       /* Remove the "(empty)" item, if there are other ones */
@@ -449,23 +569,28 @@ populate_or_checksum_helper (HMENU menu, Lisp_Object path, Lisp_Object desc,
 
       /* Add the header to the popup, if told so. The same as in X - an
         insensitive item, and a separator (Seems to me, there were
-        two separators in X... In Windows this looks ugly, anyways. */
-      if (!bar_p && !deep_p && popup_menu_titles && !NILP(pgui_item->name))
+        two separators in X... In Windows this looks ugly, anyways.) */
+      if (!bar_p && !deep_p && popup_menu_titles && !NILP (pgui_item->name))
        {
          CHECK_STRING (pgui_item->name);
          InsertMenu (menu, 0, MF_BYPOSITION | MF_STRING | MF_DISABLED,
-                     0, XSTRING_DATA(pgui_item->name));
+                     0, displayable_menu_item (gui_item, bar_p, NULL));
          InsertMenu (menu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
-         SetMenuDefaultItem (menu, 0, MF_BYPOSITION);
+         if (xSetMenuDefaultItem) /* not in NT 3.5x */
+           xSetMenuDefaultItem (menu, 0, MF_BYPOSITION);
        }
     }
-  UNGCPRO; /* gui_item */
+
+  if (bar_p)
+    Fputhash (Qt, accel_list, hash_tab);
+
+  UNGCPRO;
   return checksum;
 }
 
 static void
 populate_menu (HMENU menu, Lisp_Object path, Lisp_Object desc,
-                            Lisp_Object hash_tab, int bar_p)
+              Lisp_Object hash_tab, int bar_p)
 {
   populate_or_checksum_helper (menu, path, desc, hash_tab, bar_p, 1);
 }
@@ -477,23 +602,28 @@ checksum_menu (Lisp_Object desc)
 }
 
 static void
-update_frame_menubar_maybe (struct frame* f)
+update_frame_menubar_maybe (struct frame *f)
 {
   HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f));
   struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
   Lisp_Object desc = (!NILP (w->menubar_visible_p)
                      ? symbol_value_in_buffer (Qcurrent_menubar, w->buffer)
                      : Qnil);
+  struct gcpro gcpro1;
+
+  GCPRO1 (desc); /* it's safest to do this, just in case some filter
+                   or something changes the value of current-menubar */
 
   top_level_menu = menubar;
 
   if (NILP (desc) && menubar != NULL)
     {
       /* Menubar has gone */
-      FRAME_MSWINDOWS_MENU_HASH_TABLE(f) = Qnil;
+      FRAME_MSWINDOWS_MENU_HASH_TABLE (f) = Qnil;
       SetMenu (FRAME_MSWINDOWS_HANDLE (f), NULL);
       DestroyMenu (menubar);
       DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f));
+      UNGCPRO;
       return;
     }
 
@@ -507,29 +637,35 @@ update_frame_menubar_maybe (struct frame* f)
   if (NILP (desc))
     {
       /* We did not have the bar and are not going to */
+      UNGCPRO;
       return;
     }
 
   /* Now we bail out if the menubar has not changed */
-  if (FRAME_MSWINDOWS_MENU_CHECKSUM(f) == checksum_menu (desc))
-    return;
+  if (FRAME_MSWINDOWS_MENU_CHECKSUM (f) == checksum_menu (desc))
+    {
+      UNGCPRO;
+      return;
+    }
 
 populate:
   /* Come with empty hash table */
-  if (NILP (FRAME_MSWINDOWS_MENU_HASH_TABLE(f)))
-    FRAME_MSWINDOWS_MENU_HASH_TABLE(f) =
+  if (NILP (FRAME_MSWINDOWS_MENU_HASH_TABLE (f)))
+    FRAME_MSWINDOWS_MENU_HASH_TABLE (f) =
       make_lisp_hash_table (50, HASH_TABLE_NON_WEAK, HASH_TABLE_EQUAL);
   else
-    Fclrhash (FRAME_MSWINDOWS_MENU_HASH_TABLE(f));
+    Fclrhash (FRAME_MSWINDOWS_MENU_HASH_TABLE (f));
 
   Fputhash (hmenu_to_lisp_object (menubar), Qnil,
-           FRAME_MSWINDOWS_MENU_HASH_TABLE(f));
+           FRAME_MSWINDOWS_MENU_HASH_TABLE (f));
   populate_menu (menubar, Qnil, desc,
-                FRAME_MSWINDOWS_MENU_HASH_TABLE(f), 1);
+                FRAME_MSWINDOWS_MENU_HASH_TABLE (f), 1);
   SetMenu (FRAME_MSWINDOWS_HANDLE (f), menubar);
   DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f));
 
-  FRAME_MSWINDOWS_MENU_CHECKSUM(f) = checksum_menu (desc);
+  FRAME_MSWINDOWS_MENU_CHECKSUM (f) = checksum_menu (desc);
+
+  UNGCPRO;
 }
 
 static void
@@ -537,6 +673,8 @@ prune_menubar (struct frame *f)
 {
   HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f));
   Lisp_Object desc = current_frame_menubar (f);
+  struct gcpro gcpro1;
+
   if (menubar == NULL)
     return;
 
@@ -544,20 +682,22 @@ prune_menubar (struct frame *f)
      triggers. To resolve, we must prevent filters explicitly from
      mangling with the active menu. In apply_filter probably?
      Is copy-tree on the whole menu too expensive? */
-  if (NILP(desc))
+  if (NILP (desc))
     /* abort(); */
     return;
 
+  GCPRO1 (desc); /* just to be safe -- see above */
   /* We do the trick by removing all items and re-populating top level */
   empty_menu (menubar, 0);
 
-  assert (HASH_TABLEP (FRAME_MSWINDOWS_MENU_HASH_TABLE(f)));
-  Fclrhash (FRAME_MSWINDOWS_MENU_HASH_TABLE(f));
+  assert (HASH_TABLEP (FRAME_MSWINDOWS_MENU_HASH_TABLE (f)));
+  Fclrhash (FRAME_MSWINDOWS_MENU_HASH_TABLE (f));
 
   Fputhash (hmenu_to_lisp_object (menubar), Qnil,
-           FRAME_MSWINDOWS_MENU_HASH_TABLE(f));
-  populate_menu (menubar, Qnil, desc, 
-                FRAME_MSWINDOWS_MENU_HASH_TABLE(f), 1);
+           FRAME_MSWINDOWS_MENU_HASH_TABLE (f));
+  populate_menu (menubar, Qnil, desc,
+                FRAME_MSWINDOWS_MENU_HASH_TABLE (f), 1);
+  UNGCPRO;
 }
 
 /*
@@ -572,13 +712,25 @@ menu_cleanup (struct frame *f)
   current_hash_table = Qnil;
   prune_menubar (f);
 }
-  
+
+int
+mswindows_char_is_accelerator (struct frame *f, Emchar ch)
+{
+  Lisp_Object hash = FRAME_MSWINDOWS_MENU_HASH_TABLE (f);
+
+  if (NILP (hash))
+    return 0;
+  /* !!#### not Mule-ized */
+  return !NILP (memq_no_quit (make_char (tolower (ch)),
+                             Fgethash (Qt, hash, Qnil)));
+}
+
 \f
 /*------------------------------------------------------------------------*/
 /* Message handlers                                                       */
 /*------------------------------------------------------------------------*/
 static Lisp_Object
-unsafe_handle_wm_initmenupopup_1 (HMENU menu, struct frame* f)
+unsafe_handle_wm_initmenupopup_1 (HMENU menu, struct frame *f)
 {
   /* This function can call lisp, beat dogs and stick chewing gum to
      everything! */
@@ -610,7 +762,7 @@ unsafe_handle_wm_initmenupopup_1 (HMENU menu, struct frame* f)
 }
 
 static Lisp_Object
-unsafe_handle_wm_initmenu_1 (struct frame* f)
+unsafe_handle_wm_initmenu_1 (struct frame *f)
 {
   /* This function can call lisp */
 
@@ -627,7 +779,7 @@ unsafe_handle_wm_initmenu_1 (struct frame* f)
   update_frame_menubar_maybe (f);
 
   current_menudesc = current_frame_menubar (f);
-  current_hash_table = FRAME_MSWINDOWS_MENU_HASH_TABLE(f);
+  current_hash_table = FRAME_MSWINDOWS_MENU_HASH_TABLE (f);
   assert (HASH_TABLEP (current_hash_table));
 
   return Qt;
@@ -640,7 +792,7 @@ unsafe_handle_wm_initmenu_1 (struct frame* f)
  * command if we return nil
  */
 Lisp_Object
-mswindows_handle_wm_command (struct frame* f, WORD id)
+mswindows_handle_wm_command (struct frame *f, WORD id)
 {
   /* Try to map the command id through the proper hash table */
   Lisp_Object data, fn, arg, frame;
@@ -667,7 +819,7 @@ mswindows_handle_wm_command (struct frame* f, WORD id)
   XSETFRAME (frame, f);
   /* this used to call mswindows_enqueue_misc_user_event but that
      breaks customize because the misc_event gets eval'ed in some
-     cicumstances. Don't change it back unless you can fix the
+     circumstances. Don't change it back unless you can fix the
      customize problem also.*/
   enqueue_misc_user_event (frame, fn, arg);
   mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
@@ -682,7 +834,7 @@ mswindows_handle_wm_command (struct frame* f, WORD id)
 /*------------------------------------------------------------------------*/
 
 static HMENU wm_initmenu_menu;
-static struct frame* wm_initmenu_frame;
+static struct frame *wm_initmenu_frame;
 
 static Lisp_Object
 unsafe_handle_wm_initmenupopup (Lisp_Object u_n_u_s_e_d)
@@ -697,7 +849,7 @@ unsafe_handle_wm_initmenu (Lisp_Object u_n_u_s_e_d)
 }
 
 Lisp_Object
-mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame* frm)
+mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame *frm)
 {
   /* We cannot pass hmenu as a lisp object. Use static var */
   wm_initmenu_menu = hmenu;
@@ -706,10 +858,10 @@ mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame* frm)
 }
 
 Lisp_Object
-mswindows_handle_wm_initmenu (HMENU hmenu, struct frame* f)
+mswindows_handle_wm_initmenu (HMENU hmenu, struct frame *f)
 {
   /* Handle only frame menubar, ignore if from popup or system menu */
-  if (GetMenu (FRAME_MSWINDOWS_HANDLE(f)) == hmenu)
+  if (GetMenu (FRAME_MSWINDOWS_HANDLE (f)) == hmenu)
     {
       wm_initmenu_frame = f;
       return mswindows_protect_modal_loop (unsafe_handle_wm_initmenu, Qnil);
@@ -723,25 +875,28 @@ mswindows_handle_wm_initmenu (HMENU hmenu, struct frame* f)
 /*------------------------------------------------------------------------*/
 
 static void
-mswindows_update_frame_menubars (struct frame* f)
+mswindows_update_frame_menubars (struct frame *f)
 {
   update_frame_menubar_maybe (f);
 }
 
 static void
-mswindows_free_frame_menubars (struct frame* f)
+mswindows_free_frame_menubars (struct frame *f)
 {
-  FRAME_MSWINDOWS_MENU_HASH_TABLE(f) = Qnil;
+  FRAME_MSWINDOWS_MENU_HASH_TABLE (f) = Qnil;
 }
 
 static void
 mswindows_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
 {
   struct frame *f = selected_frame ();
-  struct Lisp_Event *eev = NULL;
+  Lisp_Event *eev = NULL;
   HMENU menu;
   POINT pt;
   int ok;
+  struct gcpro gcpro1;
+
+  GCPRO1 (menu_desc); /* to be safe -- see above */
 
   if (!NILP (event))
     {
@@ -779,14 +934,14 @@ mswindows_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
   current_menudesc = menu_desc;
   current_hash_table =
     make_lisp_hash_table (10, HASH_TABLE_NON_WEAK, HASH_TABLE_EQUAL);
-  menu = create_empty_popup_menu();
+  menu = create_empty_popup_menu ();
   Fputhash (hmenu_to_lisp_object (menu), Qnil, current_hash_table);
   top_level_menu = menu;
-  
+
   /* see comments in menubar-x.c */
   if (zmacs_regions)
     zmacs_region_stays = 1;
-  
+
   ok = TrackPopupMenu (menu,
                       TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
                       pt.x, pt.y, 0,
@@ -798,11 +953,13 @@ mswindows_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
   mswindows_unmodalize_signal_maybe ();
 
   /* This is probably the only real reason for failure */
-  if (!ok) {
-    menu_cleanup (f);
-    signal_simple_error ("Cannot track popup menu while in menu",
-                        menu_desc);
-  }
+  if (!ok)
+    {
+      menu_cleanup (f);
+      signal_simple_error ("Cannot track popup menu while in menu",
+                          menu_desc);
+    }
+  UNGCPRO;
 }
 
 \f