(update_frame_tool_bar): Use -1 as index
[bpt/emacs.git] / src / w32menu.c
index 32441e4..f0d582d 100644 (file)
@@ -1,11 +1,12 @@
 /* Menu support for GNU Emacs on the Microsoft W32 API.
-   Copyright (C) 1986,88,93,94,96,98,1999,2003  Free Software Foundation, Inc.
+   Copyright (C) 1986, 1988, 1993, 1994, 1996, 1998, 1999, 2001, 2002,
+                 2003, 2004, 2005, 2006, 2007  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 GNU Emacs 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)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
@@ -15,13 +16,14 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 #include <config.h>
 #include <signal.h>
 
 #include <stdio.h>
+#include <mbstring.h>
 #include "lisp.h"
 #include "termhooks.h"
 #include "keyboard.h"
@@ -128,7 +130,7 @@ typedef struct _widget_value
 #define FALSE 0
 #endif /* no TRUE */
 
-static HMENU current_popup_menu;
+HMENU current_popup_menu;
 
 void syms_of_w32menu ();
 void globals_of_w32menu ();
@@ -148,10 +150,10 @@ GetMenuItemInfoA_Proc get_menu_item_info = NULL;
 SetMenuItemInfoA_Proc set_menu_item_info = NULL;
 AppendMenuW_Proc unicode_append_menu = NULL;
 
-Lisp_Object Vmenu_updating_frame;
-
 Lisp_Object Qdebug_on_next_call;
 
+extern Lisp_Object Vmenu_updating_frame;
+
 extern Lisp_Object Qmenu_bar;
 
 extern Lisp_Object QCtoggle, QCradio;
@@ -234,10 +236,6 @@ static int menu_items_n_panes;
 /* Current depth within submenus.  */
 static int menu_items_submenu_depth;
 
-/* Flag which when set indicates a dialog or menu has been posted by
-   Xt on behalf of one of the widget sets.  */
-static int popup_activated_flag;
-
 static int next_menubar_widget_id;
 
 /* This is set nonzero after the user activates the menu bar, and set
@@ -737,7 +735,8 @@ cached information about equivalent key sequences.  */)
 
       XSETFRAME (Vmenu_updating_frame, f);
     }
-  Vmenu_updating_frame = Qnil;
+  else
+    Vmenu_updating_frame = Qnil;
 #endif /* HAVE_MENUS */
 
   title = Qnil;
@@ -817,8 +816,10 @@ cached information about equivalent key sequences.  */)
     }
 
 #ifdef HAVE_MENUS
-  /* If resources from a previous popup menu exist yet, does nothing
-     until the `menu_free_timer' has freed them (see w32fns.c).
+  /* If resources from a previous popup menu still exist, does nothing
+     until the `menu_free_timer' has freed them (see w32fns.c). This
+     can occur if you press ESC or click outside a menu without selecting
+     a menu item.
   */
   if (current_popup_menu)
     {
@@ -835,6 +836,7 @@ cached information about equivalent key sequences.  */)
   UNBLOCK_INPUT;
 
   discard_menu_items ();
+
 #endif /* HAVE_MENUS */
 
   UNGCPRO;
@@ -845,7 +847,7 @@ cached information about equivalent key sequences.  */)
 
 #ifdef HAVE_MENUS
 
-DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0,
+DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
        doc: /* Pop up a dialog box and return user's selection.
 POSITION specifies which frame to use.
 This is normally a mouse button event or a window or frame.
@@ -1065,11 +1067,10 @@ menubar_selection_callback (FRAME_PTR f, void * client_data)
              buf.kind = MENU_BAR_EVENT;
              buf.frame_or_window = frame;
              buf.arg = entry;
-             kbd_buffer_store_event (&buf);
-
              /* Free memory used by owner-drawn and help-echo strings.  */
              w32_free_menu_strings (FRAME_W32_WINDOW (f));
-             f->output_data.w32->menu_command_in_progress = 0;
+             kbd_buffer_store_event (&buf);
+
              f->output_data.w32->menubar_active = 0;
              return;
            }
@@ -1078,7 +1079,6 @@ menubar_selection_callback (FRAME_PTR f, void * client_data)
     }
   /* Free memory used by owner-drawn and help-echo strings.  */
   w32_free_menu_strings (FRAME_W32_WINDOW (f));
-  f->output_data.w32->menu_command_in_progress = 0;
   f->output_data.w32->menubar_active = 0;
 }
 
@@ -1443,7 +1443,8 @@ set_frame_menubar (f, first_time, deep_p)
         because it is not reentrant.  */
       specbind (Qdebug_on_next_call, Qnil);
 
-      record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil));
+      record_unwind_save_match_data ();
+
       if (NILP (Voverriding_local_map_menu_flag))
        {
          specbind (Qoverriding_terminal_local_map, Qnil);
@@ -1935,6 +1936,10 @@ w32_menu_show (f, x, y, for_click, keymaps, title, error)
 
   DestroyMenu (menu);
 
+  /* Free the owner-drawn and help-echo menu strings.  */
+  w32_free_menu_strings (FRAME_W32_WINDOW (f));
+  f->output_data.w32->menubar_active = 0;
+
   /* Find the selected item, and its pane, to return
      the proper value.  */
   if (menu_item_selection != 0)
@@ -1987,6 +1992,9 @@ w32_menu_show (f, x, y, for_click, keymaps, title, error)
            }
        }
     }
+  else if (!for_click)
+    /* Make "Cancel" equivalent to C-g.  */
+    Fsignal (Qquit, Qnil);
 
   return Qnil;
 }
@@ -2138,7 +2146,6 @@ w32_dialog_show (f, keymaps, title, header, error)
 
   /* Display the menu.  */
   lw_pop_up_all_widgets (dialog_id);
-  popup_activated_flag = 1;
 
   /* Process events that apply to the menu.  */
   popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id);
@@ -2179,6 +2186,9 @@ w32_dialog_show (f, keymaps, title, header, error)
            }
        }
     }
+  else
+    /* Make "Cancel" equivalent to C-g.  */
+    Fsignal (Qquit, Qnil);
 
   return Qnil;
 }
@@ -2252,8 +2262,9 @@ static int
 add_menu_item (HMENU menu, widget_value *wv, HMENU item)
 {
   UINT fuFlags;
-  char *out_string;
+  char *out_string, *p, *q;
   int return_value;
+  size_t nlen, orig_len;
 
   if (name_is_separator (wv->name))
     {
@@ -2277,6 +2288,57 @@ add_menu_item (HMENU menu, widget_value *wv, HMENU item)
       else
        out_string = wv->name;
 
+      /* Quote any special characters within the menu item's text and
+        key binding.  */
+      nlen = orig_len = strlen (out_string);
+      if (unicode_append_menu)
+        {
+          /* With UTF-8, & cannot be part of a multibyte character.  */
+          for (p = out_string; *p; p++)
+            {
+              if (*p == '&')
+                nlen++;
+            }
+        }
+      else
+        {
+          /* If encoded with the system codepage, use multibyte string
+             functions in case of multibyte characters that contain '&'.  */
+          for (p = out_string; *p; p = _mbsinc (p))
+            {
+              if (_mbsnextc (p) == '&')
+                nlen++;
+            }
+        }
+
+      if (nlen > orig_len)
+        {
+          p = out_string;
+          out_string = alloca (nlen + 1);
+          q = out_string;
+          while (*p)
+            {
+              if (unicode_append_menu)
+                {
+                  if (*p == '&')
+                    *q++ = *p;
+                  *q++ = *p++;
+                }
+              else
+                {
+                  if (_mbsnextc (p) == '&')
+                    {
+                      _mbsncpy (q, p, 1);
+                      q = _mbsinc (q);
+                    }
+                  _mbsncpy (q, p, 1);
+                  p = _mbsinc (p);
+                  q = _mbsinc (q);
+                }
+            }
+          *q = '\0';
+        }
+
       if (item != NULL)
        fuFlags = MF_POPUP;
       else if (wv->title || wv->call_data == 0)
@@ -2319,7 +2381,23 @@ add_menu_item (HMENU menu, widget_value *wv, HMENU item)
                                          item != NULL ? (UINT) item
                                            : (UINT) wv->call_data,
                                          utf16_string);
-      if (fuFlags & MF_OWNERDRAW)
+      if (!return_value)
+       {
+         /* On W9x/ME, unicode menus are not supported, though AppendMenuW
+            apparently does exist at least in some cases and appears to be
+            stubbed out to do nothing.  out_string is UTF-8, but since
+            our standard menus are in English and this is only going to
+            happen the first time a menu is used, the encoding is
+            of minor importance compared with menus not working at all.  */
+         return_value =
+           AppendMenu (menu, fuFlags,
+                       item != NULL ? (UINT) item: (UINT) wv->call_data,
+                       out_string);
+         /* Don't use unicode menus in future.  */
+         unicode_append_menu = NULL;
+       }
+
+      if (unicode_append_menu && (fuFlags & MF_OWNERDRAW))
        local_free (out_string);
     }
   else
@@ -2399,13 +2477,6 @@ fill_in_menu (HMENU menu, widget_value *wv)
   return 1;
 }
 
-int
-popup_activated ()
-{
-  /* popup_activated_flag not actually used on W32 */
-  return 0;
-}
-
 /* Display help string for currently pointed to menu item. Not
    supported on NT 3.51 and earlier, as GetMenuItemInfo is not
    available. */
@@ -2417,8 +2488,11 @@ w32_menu_display_help (HWND owner, HMENU menu, UINT item, UINT flags)
       struct frame *f = x_window_to_frame (&one_w32_display_info, owner);
       Lisp_Object frame, help;
 
-      /* No help echo on owner-draw menu items.  */
-      if (flags & MF_OWNERDRAW || flags & MF_POPUP)
+      /* No help echo on owner-draw menu items, or when the keyboard is used
+        to navigate the menus, since tooltips are distracting if they pop
+        up elsewhere.  */
+      if (flags & MF_OWNERDRAW || flags & MF_POPUP
+         || !(flags & MF_MOUSESELECT))
        help = Qnil;
       else
        {
@@ -2505,6 +2579,21 @@ w32_free_menu_strings (hwnd)
 
 #endif /* HAVE_MENUS */
 
+/* The following is used by delayed window autoselection.  */
+
+DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
+       doc: /* Return t if a menu or popup dialog is active on selected frame.  */)
+     ()
+{
+#ifdef HAVE_MENUS
+  FRAME_PTR f;
+  f = SELECTED_FRAME ();
+  return (f->output_data.w32->menubar_active > 0) ? Qt : Qnil;
+#else
+  return Qnil;
+#endif /* HAVE_MENUS */
+}
+
 void syms_of_w32menu ()
 {
        globals_of_w32menu ();
@@ -2516,12 +2605,8 @@ void syms_of_w32menu ()
   Qdebug_on_next_call = intern ("debug-on-next-call");
   staticpro (&Qdebug_on_next_call);
 
-  DEFVAR_LISP ("menu-updating-frame", &Vmenu_updating_frame,
-              doc: /* Frame for which we are updating a menu.
-The enable predicate for a menu command should check this variable.  */);
-  Vmenu_updating_frame = Qnil;
-
   defsubr (&Sx_popup_menu);
+  defsubr (&Smenu_or_popup_active_p);
 #ifdef HAVE_MENUS
   defsubr (&Sx_popup_dialog);
 #endif