changeset 41733:917737801660

(current_popup_menu, get_menu_item_info): (set_menu_item_info): New vars. (set_frame_menubar): Doc fix clarifying GC interaction with menus. (w32_menu_show): Set current_popup_menu. (add_menu_item): Allocate new strings for owner-drawn menu items and help strings. Use owner-draw for disabled menu items again. (w32_menu_display_help): Ignore owner-drawn items and popup menus. (w32_free_submenu_strings, w32_free_menu_strings): New functions.
author Jason Rumney <jasonr@gnu.org>
date Sat, 01 Dec 2001 11:14:23 +0000
parents 0d9b74cd27cd
children 6cf3e8b82de8
files src/w32menu.c
diffstat 1 files changed, 96 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/src/w32menu.c	Sat Dec 01 11:13:25 2001 +0000
+++ b/src/w32menu.c	Sat Dec 01 11:14:23 2001 +0000
@@ -119,6 +119,11 @@
 #define FALSE 0
 #endif /* no TRUE */
 
+static HMENU current_popup_menu;
+
+FARPROC get_menu_item_info;
+FARPROC set_menu_item_info;
+
 Lisp_Object Vmenu_updating_frame;
 
 Lisp_Object Qdebug_on_next_call;
@@ -1416,7 +1421,10 @@
 	}
 
       /* Now GC cannot happen during the lifetime of the widget_value,
-	 so it's safe to store data from a Lisp_String.  */
+	 so it's safe to store data from a Lisp_String, as long as
+	 local copies are made when the actual menu is created.
+	 Windows takes care of this for normal string items, but
+	 not for owner-drawn items or additional item-info.  */
       wv = first_wv->contents;
       for (i = 0; i < XVECTOR (items)->size; i += 4)
 	{
@@ -1752,7 +1760,7 @@
     }
 
   /* Actually create the menu.  */
-  menu = CreatePopupMenu ();
+  current_popup_menu = menu = CreatePopupMenu ();
   fill_in_menu (menu, first_wv->contents);
 
   /* Adjust coordinates to be root-window-relative.  */
@@ -1836,6 +1844,7 @@
 }
 
 
+#ifdef HAVE_DIALOGS
 static char * button_names [] = {
   "button1", "button2", "button3", "button4", "button5",
   "button6", "button7", "button8", "button9", "button10" };
@@ -1959,13 +1968,11 @@
   }
 
   /* Actually create the dialog.  */
-#ifdef HAVE_DIALOGS
   dialog_id = widget_id_tick++;
   menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
 			   f->output_data.w32->widget, 1, 0,
 			   dialog_selection_callback, 0);
   lw_modify_all_widgets (dialog_id, first_wv->contents, TRUE);
-#endif
 
   /* Free the widget_value objects we used to specify the contents.  */
   free_menubar_widget_value_tree (first_wv);
@@ -1974,7 +1981,6 @@
   menu_item_selection = 0;
 
   /* Display the menu.  */
-#ifdef HAVE_DIALOGS
   lw_pop_up_all_widgets (dialog_id);
   popup_activated_flag = 1;
 
@@ -1982,7 +1988,6 @@
   popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id);
 
   lw_destroy_all_widgets (dialog_id); 
-#endif
 
   /* Find the selected item, and its pane, to return
      the proper value.  */
@@ -2023,6 +2028,7 @@
 
   return Qnil;
 }
+#endif  /* HAVE_DIALOGS  */
 
 
 /* Is this item a separator? */
@@ -2077,16 +2083,19 @@
       else
 	out_string = wv->name;
 
-      if (wv->title)
+      if (wv->title || wv->call_data == 0)
 	{
-#if 0  /* no GC while popup menu is active */
-	  out_string = LocalAlloc (0, strlen (wv->name) + 1);
-	  strcpy (out_string, wv->name);
-#endif
-	  fuFlags = MF_OWNERDRAW | MF_DISABLED;
+	  /* Only use MF_OWNERDRAW if GetMenuItemInfo is usable, since
+	     we can't deallocate the memory otherwise.  */
+	  if (get_menu_item_info)
+	    {
+	      out_string = LocalAlloc (LPTR, strlen (wv->name) + 1);
+	      strcpy (out_string, wv->name);
+	      fuFlags = MF_OWNERDRAW | MF_DISABLED;
+	    }
+	  else
+	    fuFlags = MF_DISABLED;
 	}
-      else if (wv->call_data == 0)
-	fuFlags |= MF_DISABLED;
 
       /* Draw radio buttons and tickboxes. */
       else if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
@@ -2108,9 +2117,6 @@
   /* This must be done after the menu item is created.  */
   if (!wv->title && wv->call_data != 0)
     {
-      HMODULE user32 = GetModuleHandle ("user32.dll");
-      FARPROC set_menu_item_info = GetProcAddress (user32, "SetMenuItemInfoA");
-
       if (set_menu_item_info)
 	{
 	  MENUITEMINFO info;
@@ -2118,8 +2124,15 @@
 	  info.cbSize = sizeof (info);
 	  info.fMask = MIIM_DATA;
 
-	  /* Set help string for menu item.  */
-	  info.dwItemData = (DWORD)wv->help;
+	  /* Set help string for menu item.  Allocate new memory
+	     from the heap for it, since garbage collection can
+	     occur while menus are active.  */
+	  if (wv->help)
+	    {
+	      info.dwItemData
+		= (DWORD) LocalAlloc (LPTR, strlen(wv->help) + 1);
+	      strcpy (info.dwItemData, wv->help);
+	    }
 
 	  if (wv->button_type == BUTTON_TYPE_RADIO)
 	    {
@@ -2183,21 +2196,25 @@
 void
 w32_menu_display_help (HWND owner, HMENU menu, UINT item, UINT flags)
 {
-  HMODULE user32 = GetModuleHandle ("user32.dll");
-  FARPROC get_menu_item_info = GetProcAddress (user32, "GetMenuItemInfoA");
-
   if (get_menu_item_info)
     {
-      MENUITEMINFO info;
       struct frame *f = x_window_to_frame (&one_w32_display_info, owner);
       Lisp_Object frame, help;
 
-      bzero (&info, sizeof (info));
-      info.cbSize = sizeof (info);
-      info.fMask = MIIM_DATA;
-      get_menu_item_info (menu, item, FALSE, &info);
+      // No help echo on owner-draw menu items.
+      if (flags & MF_OWNERDRAW || flags & MF_POPUP)
+	help = Qnil;
+      else
+	{
+	  MENUITEMINFO info;
 
-      help = info.dwItemData ? build_string ((char *)info.dwItemData) : Qnil;
+	  bzero (&info, sizeof (info));
+	  info.cbSize = sizeof (info);
+	  info.fMask = MIIM_DATA;
+	  get_menu_item_info (menu, item, FALSE, &info);
+
+	  help = info.dwItemData ? build_string ((char *)info.dwItemData) : Qnil;
+	}
 
       /* Store the help echo in the keyboard buffer as the X toolkit
 	 version does, rather than directly showing it. This seems to
@@ -2215,15 +2232,66 @@
     }
 }
 
+/* Free memory used by owner-drawn and help_echo strings.  */
+static void
+w32_free_submenu_strings (menu)
+     HMENU menu;
+{
+  int i, num = GetMenuItemCount (menu);
+  for (i = 0; i < num; i++)
+    {
+      MENUITEMINFO info;
 
+      info.cbSize = sizeof (info);
+      info.fMask = MIIM_DATA | MIIM_SUBMENU;
+
+      get_menu_item_info (menu, i, TRUE, &info);
+
+      /* Both owner-drawn names and help strings are held in dwItemData.  */
+      if (info.dwItemData)
+	LocalFree (info.dwItemData);
+
+      /* Recurse down submenus.  */
+      if (info.hSubMenu)
+	w32_free_submenu_strings (info.hSubMenu);
+    }
+}
+
+void
+w32_free_menu_strings (hwnd)
+     HWND hwnd;
+{
+  HMENU menu = current_popup_menu;
+
+  if (get_menu_item_info)
+    {
+      /* If there is no popup menu active, free the strings from the frame's
+	 menubar.  */
+      if (!menu)
+	menu = GetMenu (hwnd);
+
+      if (menu)
+	w32_free_submenu_strings (menu);
+    }
+
+  current_popup_menu = NULL;
+}
 
 #endif /* HAVE_MENUS */
+
 
 syms_of_w32menu ()
 {
+  /* See if Get/SetMenuItemInfo functions are available.  */
+  HMODULE user32 = GetModuleHandle ("user32.dll");
+  get_menu_item_info = GetProcAddress (user32, "GetMenuItemInfoA");
+  set_menu_item_info = GetProcAddress (user32, "SetMenuItemInfoA");
+
   staticpro (&menu_items);
   menu_items = Qnil;
 
+  current_popup_menu = NULL;
+
   Qdebug_on_next_call = intern ("debug-on-next-call");
   staticpro (&Qdebug_on_next_call);