diff src/menubar-msw.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 8de8e3f6228a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/menubar-msw.c	Mon Aug 13 11:28:15 2007 +0200
@@ -0,0 +1,831 @@
+/* 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>
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Not in FSF. */
+
+/* Author:
+   Initially written by kkm 12/24/97,
+   peeking into and copying stuff from menubar-x.c
+   */
+
+/* Algorithm for handling menus is as follows. When window's menubar
+ * is created, current-menubar is not traversed in depth. Rather, only
+ * top level items, both items and pulldowns, are added to the
+ * menubar. Each pulldown is initially empty. When a pulldown is
+ * selected and about to open, corresponding element of
+ * current-menubar is found, and the newly open pulldown is
+ * populated. This is made again in the same non-recursive manner.
+ *
+ * This algorithm uses hash tables to find out element of the menu
+ * descriptor list given menu handle. The key is an opaque ptr data
+ * type, keeping menu handle, and the value is a list of strings
+ * representing the path from the root of the menu to the item
+ * descriptor. Each frame has an associated hash table.
+ *
+ * Leaf items are assigned a unique id based on item's hash. When an
+ * item is selected, Windows sends back the id. Unfortunately, only
+ * low 16 bit of the ID are sent, and there's no way to get the 32-bit
+ * value. Yes, Win32 is just a different set of bugs than X! Aside
+ * from this blame, another hashing mechanism is required to map menu
+ * ids to commands (which are actually Lisp_Object's). This mapping is
+ * performed in the same hash table, as the lifetime of both maps is
+ * exactly the same. This is unambigous, as menu handles are
+ * represented by lisp opaques, while command ids are by lisp
+ * integers. The additional advantage for this is that command forms
+ * are automatically GC-protected, which is important because these
+ * may be transient forms generated by :filter functions.
+ *
+ * The hash table is not allowed to grow too much; it is pruned
+ * whenever this is safe to do. This is done by re-creating the menu
+ * bar, and clearing and refilling the hash table from scratch.
+ *
+ * Popup menus are handled identically to pulldowns. A static hash
+ * table is used for popup menus, and lookup is made not in
+ * current-menubar but in a lisp form supplied to the `popup'
+ * function.
+ *
+ * Another Windows weirdness is that there's no way to tell that a
+ * popup has been dismissed without making selection. We need to know
+ * that to cleanup the popup menu hash table, but this is not honestly
+ * doable using *documented* sequence of messages. Sticking to
+ * 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,
+ * anyways!
+ *
+ */
+
+#include <config.h>
+#include "lisp.h"
+#include <limits.h>
+
+#include "buffer.h"
+#include "commands.h"
+#include "console-msw.h"
+#include "elhash.h"
+#include "events.h"
+#include "frame.h"
+#include "gui.h"
+#include "lisp.h"
+#include "menubar.h"
+#include "menubar-msw.h"
+#include "opaque.h"
+#include "window.h"
+
+/* #### */
+#define REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIHGT_FLUSH 0
+
+#define EMPTY_ITEM_ID ((UINT)LISP_TO_VOID (Qunbound))
+#define EMPTY_ITEM_NAME "(empty)"
+
+/* Current menu (bar or popup) descriptor. gcpro'ed */
+static Lisp_Object current_menudesc;
+
+/* Current menubar or popup hash table. gcpro'ed */
+static Lisp_Object current_hash_table;
+
+/* This is used to allocate unique ids to menu items.
+   Items ids are in MENU_ITEM_ID_MIN to MENU_ITEM_ID_MAX.
+   Allocation checks that the item is not already in
+   the TOP_LEVEL_MENU */
+
+/* #### defines go to gui-msw.h, as the range is shared with toolbars
+   (If only toolbars will be implemented as common controls) */
+#define MENU_ITEM_ID_MIN 0x8000
+#define MENU_ITEM_ID_MAX 0xFFFF
+#define MENU_ITEM_ID_BITS(x) (((x) & 0x7FFF) | 0x8000)
+static HMENU top_level_menu;
+
+/*
+ * This returns Windows-style menu item string:
+ * "Left Flush\tRight Flush"
+ */
+static char*
+displayable_menu_item (Lisp_Object gui_item, int bar_p)
+{
+  /* 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++;
+    }
+
+  /* Right flush part, unless we're at the top-level where it's not allowed */
+  if (!bar_p)
+    {
+      assert (MAX_MENUITEM_LENGTH > ll + 1);
+      lr = gui_item_display_flush_right (gui_item, buf + ll + 1,
+					 MAX_MENUITEM_LENGTH - ll - 1);
+      if (lr)
+	buf [ll] = '\t';
+     }
+
+  return buf;
+}
+
+/*
+ * hmenu_to_lisp_object() returns an opaque ptr given menu handle.
+ */
+static Lisp_Object
+hmenu_to_lisp_object (HMENU hmenu)
+{
+  return make_opaque_ptr (hmenu);
+}
+
+/*
+ * Allocation tries a hash based on item's path and name first. This
+ * almost guarantees that the same item will override its old value in
+ * the hash table rather than abandon it.
+ */
+static Lisp_Object
+allocate_menu_item_id (Lisp_Object path, Lisp_Object name, Lisp_Object suffix)
+{
+  UINT id = MENU_ITEM_ID_BITS (HASH3 (internal_hash (path, 0),
+				      internal_hash (name, 0),
+				      internal_hash (suffix, 0)));
+  do {
+      id = MENU_ITEM_ID_BITS (id + 1);
+  } while (GetMenuState (top_level_menu, id, MF_BYCOMMAND) != 0xFFFFFFFF);
+  return make_int (id);
+}
+
+static HMENU
+create_empty_popup_menu (void)
+{
+  return CreatePopupMenu ();
+}
+
+static void
+empty_menu (HMENU menu, int add_empty_p)
+{
+  while (DeleteMenu (menu, 0, MF_BYPOSITION));
+  if (add_empty_p)
+    AppendMenu (menu, MF_STRING | MF_GRAYED, EMPTY_ITEM_ID, EMPTY_ITEM_NAME);
+}
+
+/*
+ * The idea of checksumming is that we must hash minimal object
+ * which is necessarily changes when the item changes. For separator
+ * this is a constant, for grey strings and submenus these are hashes
+ * of names, since submenus are unpopulated until opened so always
+ * equal otherwise. For items, this is a full hash value of a callback,
+ * because a callback may me a form which can be changed only somewhere
+ * in depth.
+ */
+static unsigned long
+checksum_menu_item (Lisp_Object item)
+{
+  if (STRINGP (item))
+    {
+      /* Separator or unselectable text - hash as a string + 13 */
+      if (separator_string_p (XSTRING_DATA (item)))
+	return 13;
+      else
+	return internal_hash (item, 0) + 13;
+    }
+  else if (CONSP (item))
+    {
+      /* Submenu - hash by its string name + 0 */
+      return internal_hash (XCAR(item), 0);
+    }
+  else if (VECTORP (item))
+    {
+      /* An ordinary item - hash its name and callback form. */
+      return HASH2 (internal_hash (XVECTOR_DATA(item)[0], 0),
+		    internal_hash (XVECTOR_DATA(item)[1], 0));
+    }
+ 
+  /* An error - will be caught later */
+  return 0;
+}
+
+static void
+populate_menu_add_item (HMENU menu, Lisp_Object path,
+			Lisp_Object hash_tab, Lisp_Object item,
+			int flush_right, int bar_p)
+{
+  MENUITEMINFO item_info;
+
+  item_info.cbSize = sizeof (item_info);
+  item_info.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
+  item_info.fState = 0;
+  item_info.wID = 0;
+  item_info.fType = 0;
+
+  if (STRINGP (item))
+    {
+      /* Separator or unselectable text */
+      if (separator_string_p (XSTRING_DATA (item)))
+	item_info.fType = MFT_SEPARATOR;
+      else
+	{
+	  item_info.fType = MFT_STRING;
+	  item_info.fState = MFS_DISABLED;
+	  item_info.dwTypeData = XSTRING_DATA (item);
+	}
+    }
+  else if (CONSP (item))
+    {
+      /* Submenu */
+      HMENU submenu;
+      Lisp_Object gui_item = allocate_gui_item ();
+      struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
+      struct gcpro gcpro1;
+
+      GCPRO1 (gui_item);
+
+      menu_parse_submenu_keywords (item, gui_item);
+
+      if (!STRINGP (pgui_item->name))
+	signal_simple_error ("Menu name (first element) must be a string", item);
+
+      if (!gui_item_included_p (gui_item, Vmenubar_configuration))
+	return;
+
+      if (!gui_item_active_p (gui_item))
+	item_info.fState = MFS_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();
+
+      item_info.fMask |= MIIM_SUBMENU;
+      item_info.dwTypeData = displayable_menu_item (gui_item, bar_p);
+      item_info.hSubMenu = submenu;
+
+      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 */
+    } 
+  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;
+
+      GCPRO1 (gui_item);
+
+      if (!gui_item_included_p (gui_item, Vmenubar_configuration))
+	return;
+
+      if (!gui_item_active_p (gui_item))
+	item_info.fState = MFS_GRAYED;
+
+      style = (NILP (pgui_item->selected) || NILP (Feval (pgui_item->selected))
+	       ? Qnil : pgui_item->style);
+
+      if (EQ (style, Qradio))
+	{
+	  item_info.fType |= MFT_RADIOCHECK;
+	  item_info.fState |= MFS_CHECKED;
+	}
+      else if (EQ (style, Qtoggle))
+	{
+	  item_info.fState |= MFS_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.fType |= MFT_STRING;
+      item_info.dwTypeData = displayable_menu_item (gui_item, bar_p);
+
+      UNGCPRO; /* gui_item */
+    }
+  else
+    {
+      signal_simple_error ("Malformed menu item descriptor", item);
+    }
+
+  if (flush_right)
+    item_info.fType |= MFT_RIGHTJUSTIFY;
+
+  InsertMenuItem (menu, UINT_MAX, TRUE, &item_info);
+}  
+
+/*
+ * This function is called from populate_menu and checksum_menu.
+ * When called to populate, MENU is a menu handle, PATH is a
+ * list of strings representing menu path from root to this submenu,
+ * DESCRIPTOR is a menu descriptor, HASH_TAB is a hash table associated
+ * with root menu, BAR_P indicates whether this called for a menubar or
+ * a popup, and POPULATE_P is non-zero. Return value must be ignored.
+ * When called to checksum, DESCRIPTOR has the same meaning, POPULATE_P
+ * is zero, PATH must be Qnil, and the rest of parameters is ignored.
+ * Return value is the menu checksum.
+ */
+static unsigned long
+populate_or_checksum_helper (HMENU menu, Lisp_Object path, Lisp_Object desc,
+			     Lisp_Object hash_tab, int bar_p, int populate_p)
+{
+  Lisp_Object item_desc;
+  int deep_p, flush_right;
+  struct gcpro gcpro1;
+  unsigned long checksum;
+  Lisp_Object gui_item = allocate_gui_item ();
+  struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
+  GCPRO1 (gui_item);
+
+  /* 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;
+
+  /* Will initially contain only "(empty)" */
+  if (populate_p)
+    empty_menu (menu, 1);
+
+  /* PATH set to nil indicates top-level popup or menubar */
+  deep_p = !NILP (path);
+
+  /* Fetch keywords prepending the item list */
+  desc = menu_parse_submenu_keywords (desc, gui_item);
+
+  /* Check that menu name is specified when expected */
+  if (NILP (pgui_item->name) && deep_p)
+    signal_simple_error ("Menu must have a name", desc);
+
+  /* Apply filter if specified */
+  if (!NILP (pgui_item->filter))
+    desc = call1 (pgui_item->filter, desc);
+
+  /* Loop thru the desc's CDR and add items for each entry */
+  flush_right = 0;
+  EXTERNAL_LIST_LOOP (item_desc, 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)
+	    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);
+      else
+	checksum = HASH2 (checksum,
+			  checksum_menu_item (XCAR (item_desc)));
+    }
+  
+  if (populate_p)
+    {
+      /* Remove the "(empty)" item, if there are other ones */
+      if (GetMenuItemCount (menu) > 1)
+	RemoveMenu (menu, EMPTY_ITEM_ID, MF_BYCOMMAND);
+
+      /* 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))
+	{
+	  CHECK_STRING (pgui_item->name);
+	  InsertMenu (menu, 0, MF_BYPOSITION | MF_STRING | MF_DISABLED,
+		      0, XSTRING_DATA(pgui_item->name));
+	  InsertMenu (menu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
+	  SetMenuDefaultItem (menu, 0, MF_BYPOSITION);
+	}
+    }
+  UNGCPRO; /* gui_item */
+  return checksum;
+}
+
+static void
+populate_menu (HMENU menu, Lisp_Object path, Lisp_Object desc,
+			     Lisp_Object hash_tab, int bar_p)
+{
+  populate_or_checksum_helper (menu, path, desc, hash_tab, bar_p, 1);
+}
+
+static unsigned long
+checksum_menu (Lisp_Object desc)
+{
+  return populate_or_checksum_helper (NULL, Qnil, desc, Qunbound, 0, 0);
+}
+
+static void
+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);
+
+  top_level_menu = menubar;
+
+  if (NILP (desc) && menubar != NULL)
+    {
+      /* Menubar has gone */
+      FRAME_MSWINDOWS_MENU_HASH_TABLE(f) = Qnil;
+      SetMenu (FRAME_MSWINDOWS_HANDLE (f), NULL);
+      DestroyMenu (menubar);
+      DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f));
+      return;
+    }
+
+  if (!NILP (desc) && menubar == NULL)
+    {
+      /* Menubar has appeared */
+      menubar = CreateMenu ();
+      goto populate;
+    }
+
+  if (NILP (desc))
+    {
+      /* We did not have the bar and are not going to */
+      return;
+    }
+
+  /* Now we bail out if the menubar has not changed */
+  if (FRAME_MSWINDOWS_MENU_CHECKSUM(f) == checksum_menu (desc))
+    return;
+
+populate:
+  /* Come with empty hash table */
+  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));
+
+  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);
+  SetMenu (FRAME_MSWINDOWS_HANDLE (f), menubar);
+  DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f));
+
+  FRAME_MSWINDOWS_MENU_CHECKSUM(f) = checksum_menu (desc);
+}
+
+static void
+prune_menubar (struct frame *f)
+{
+  HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f));
+  Lisp_Object desc = current_frame_menubar (f);
+  if (menubar == NULL)
+    return;
+
+  /* #### If a filter function has set desc to Qnil, this abort()
+     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))
+    /* abort(); */
+    return;
+
+  /* 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));
+
+  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);
+}
+
+/*
+ * This is called when cleanup is possible. It is better not to
+ * clean things up at all than do it too early!
+ */
+static void
+menu_cleanup (struct frame *f)
+{
+  /* This function can GC */
+  current_menudesc = Qnil;
+  current_hash_table = Qnil;
+  prune_menubar (f);
+}
+  
+
+/*------------------------------------------------------------------------*/
+/* Message handlers                                                       */
+/*------------------------------------------------------------------------*/
+static Lisp_Object
+unsafe_handle_wm_initmenupopup_1 (HMENU menu, struct frame* f)
+{
+  /* This function can call lisp, beat dogs and stick chewing gum to
+     everything! */
+
+  Lisp_Object path, desc;
+  struct gcpro gcpro1;
+
+  /* Find which guy is going to explode */
+  path = Fgethash (hmenu_to_lisp_object (menu), current_hash_table, Qunbound);
+  assert (!UNBOUNDP (path));
+#ifdef DEBUG_XEMACS
+  /* Allow to continue in a debugger after assert - not so fatal */
+  if (UNBOUNDP (path))
+    error ("internal menu error");
+#endif
+
+  /* Now find a desc chunk for it. If none, then probably menu open
+     hook has played too much games around stuff */
+  desc = Fmenu_find_real_submenu (current_menudesc, path);
+  if (NILP (desc))
+    signal_simple_error ("This menu does not exist any more", path);
+
+  /* Now, stuff it */
+  /* DESC may be generated by filter, so we have to gcpro it */
+  GCPRO1 (desc);
+  populate_menu (menu, path, desc, current_hash_table, 0);
+  UNGCPRO;
+  return Qt;
+}
+
+static Lisp_Object
+unsafe_handle_wm_initmenu_1 (struct frame* f)
+{
+  /* This function can call lisp */
+
+  /* NOTE: This is called for the bar only, WM_INITMENU
+     for popups is filtered out */
+
+  /* #### - this menubar update mechanism is expensively anti-social and
+     the activate-menubar-hook is now mostly obsolete. */
+
+  /* We simply ignore return value. In any case, we construct the bar
+     on the fly */
+  run_hook (Qactivate_menubar_hook);
+
+  update_frame_menubar_maybe (f);
+
+  current_menudesc = current_frame_menubar (f);
+  current_hash_table = FRAME_MSWINDOWS_MENU_HASH_TABLE(f);
+  assert (HASH_TABLEP (current_hash_table));
+
+  return Qt;
+}
+
+/*
+ * Return value is Qt if we have dispatched the command,
+ * or Qnil if id has not been mapped to a callback.
+ * Window procedure may try other targets to route the
+ * command if we return nil
+ */
+Lisp_Object
+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;
+  struct gcpro gcpro1;
+
+  if (NILP (current_hash_table))
+    return Qnil;
+
+  data = Fgethash (make_int (id), current_hash_table, Qunbound);
+
+  if (UNBOUNDP (data))
+    {
+      menu_cleanup (f);
+      return Qnil;
+    }
+
+  /* Need to gcpro because the hash table may get destroyed by
+     menu_cleanup(), and will not gcpro the data any more */
+  GCPRO1 (data);
+  menu_cleanup (f);
+
+  /* Ok, this is our one. Enqueue it. */
+  get_gui_callback (data, &fn, &arg);
+  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
+     customize problem also.*/
+  enqueue_misc_user_event (frame, fn, arg);
+  mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
+
+  UNGCPRO; /* data */
+  return Qt;
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Message handling proxies                                               */
+/*------------------------------------------------------------------------*/
+
+static HMENU wm_initmenu_menu;
+static struct frame* wm_initmenu_frame;
+
+static Lisp_Object
+unsafe_handle_wm_initmenupopup (Lisp_Object u_n_u_s_e_d)
+{
+  return unsafe_handle_wm_initmenupopup_1 (wm_initmenu_menu, wm_initmenu_frame);
+}
+
+static Lisp_Object
+unsafe_handle_wm_initmenu (Lisp_Object u_n_u_s_e_d)
+{
+  return unsafe_handle_wm_initmenu_1 (wm_initmenu_frame);
+}
+
+Lisp_Object
+mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame* frm)
+{
+  /* We cannot pass hmenu as a lisp object. Use static var */
+  wm_initmenu_menu = hmenu;
+  wm_initmenu_frame = frm;
+  return mswindows_protect_modal_loop (unsafe_handle_wm_initmenupopup, Qnil);
+}
+
+Lisp_Object
+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)
+    {
+      wm_initmenu_frame = f;
+      return mswindows_protect_modal_loop (unsafe_handle_wm_initmenu, Qnil);
+    }
+  return Qt;
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Methods                                                                */
+/*------------------------------------------------------------------------*/
+
+static void
+mswindows_update_frame_menubars (struct frame* f)
+{
+  update_frame_menubar_maybe (f);
+}
+
+static void
+mswindows_free_frame_menubars (struct frame* f)
+{
+  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;
+  HMENU menu;
+  POINT pt;
+  int ok;
+
+  if (!NILP (event))
+    {
+      CHECK_LIVE_EVENT (event);
+      eev = XEVENT (event);
+      if (eev->event_type != button_press_event
+	  && eev->event_type != button_release_event)
+	wrong_type_argument (Qmouse_event_p, event);
+    }
+  else if (!NILP (Vthis_command_keys))
+    {
+      /* if an event wasn't passed, use the last event of the event sequence
+	 currently being executed, if that event is a mouse event */
+      eev = XEVENT (Vthis_command_keys); /* last event first */
+      if (eev->event_type != button_press_event
+	  && eev->event_type != button_release_event)
+	eev = NULL;
+    }
+
+  /* Default is to put the menu at the point (10, 10) in frame */
+  if (eev)
+    {
+      pt.x = eev->event.button.x;
+      pt.y = eev->event.button.y;
+      ClientToScreen (FRAME_MSWINDOWS_HANDLE (f), &pt);
+    }
+  else
+    pt.x = pt.y = 10;
+
+  if (SYMBOLP (menu_desc))
+    menu_desc = Fsymbol_value (menu_desc);
+  CHECK_CONS (menu_desc);
+  CHECK_STRING (XCAR (menu_desc));
+
+  current_menudesc = menu_desc;
+  current_hash_table =
+    make_lisp_hash_table (10, HASH_TABLE_NON_WEAK, HASH_TABLE_EQUAL);
+  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,
+		       FRAME_MSWINDOWS_HANDLE (f), NULL);
+
+  DestroyMenu (menu);
+
+  /* Signal a signal if caught by Track...() modal loop */
+  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);
+  }
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Initialization                                                         */
+/*------------------------------------------------------------------------*/
+void
+syms_of_menubar_mswindows (void)
+{
+}
+
+void
+console_type_create_menubar_mswindows (void)
+{
+  CONSOLE_HAS_METHOD (mswindows, update_frame_menubars);
+  CONSOLE_HAS_METHOD (mswindows, free_frame_menubars);
+  CONSOLE_HAS_METHOD (mswindows, popup_menu);
+}
+
+void
+vars_of_menubar_mswindows (void)
+{
+  current_menudesc = Qnil;
+  current_hash_table = Qnil;
+
+  staticpro (&current_menudesc);
+  staticpro (&current_hash_table);
+}