* dbusbind.c (xd_retrieve_arg): Pacify GCC on x86_64 GNU/Linux.
[bpt/emacs.git] / src / w32menu.c
index c5af0aa..e258ec9 100644 (file)
@@ -1,11 +1,13 @@
 /* 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, 2008
+                 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,18 +17,20 @@ 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 <signal.h>
 #include <stdio.h>
+#include <mbstring.h>
+
 #include "lisp.h"
-#include "termhooks.h"
 #include "keyboard.h"
 #include "keymap.h"
 #include "frame.h"
+#include "termhooks.h"
 #include "window.h"
 #include "blockinput.h"
 #include "buffer.h"
@@ -128,7 +132,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 +152,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 +238,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
@@ -319,14 +319,8 @@ discard_menu_items ()
 static void
 grow_menu_items ()
 {
-  Lisp_Object old;
-  int old_size = menu_items_allocated;
-  old = menu_items;
-
   menu_items_allocated *= 2;
-  menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
-  bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
-        old_size * sizeof (Lisp_Object));
+  menu_items = larger_vector (menu_items, menu_items_allocated, Qnil);
 }
 
 /* Begin a submenu.  */
@@ -574,10 +568,10 @@ list_of_panes (menu)
 
   init_menu_items ();
 
-  for (tail = menu; !NILP (tail); tail = Fcdr (tail))
+  for (tail = menu; CONSP (tail); tail = XCDR (tail))
     {
       Lisp_Object elt, pane_name, pane_data;
-      elt = Fcar (tail);
+      elt = XCAR (tail);
       pane_name = Fcar (elt);
       CHECK_STRING (pane_name);
       push_menu_pane (pane_name, Qnil);
@@ -597,9 +591,9 @@ list_of_items (pane)
 {
   Lisp_Object tail, item, item1;
 
-  for (tail = pane; !NILP (tail); tail = Fcdr (tail))
+  for (tail = pane; CONSP (tail); tail = XCDR (tail))
     {
-      item = Fcar (tail);
+      item = XCAR (tail);
       if (STRINGP (item))
        push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil, Qnil);
       else if (NILP (item))
@@ -676,8 +670,8 @@ cached information about equivalent key sequences.  */)
          enum scroll_bar_part part;
          unsigned long time;
 
-         if (mouse_position_hook)
-           (*mouse_position_hook) (&new_f, 1, &bar_window,
+         if (FRAME_TERMINAL (new_f)->mouse_position_hook)
+           (*FRAME_TERMINAL (new_f)->mouse_position_hook) (&new_f, 1, &bar_window,
                                    &part, &x, &y, &time);
          if (new_f != 0)
            XSETFRAME (window, new_f);
@@ -737,7 +731,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 +812,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 +832,7 @@ cached information about equivalent key sequences.  */)
   UNBLOCK_INPUT;
 
   discard_menu_items ();
+
 #endif /* HAVE_MENUS */
 
   UNGCPRO;
@@ -1065,11 +1063,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 +1075,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;
 }
 
@@ -1936,6 +1932,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)
@@ -1988,6 +1988,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;
 }
@@ -2139,7 +2142,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);
@@ -2180,6 +2182,9 @@ w32_dialog_show (f, keymaps, title, header, error)
            }
        }
     }
+  else
+    /* Make "Cancel" equivalent to C-g.  */
+    Fsignal (Qquit, Qnil);
 
   return Qnil;
 }
@@ -2253,8 +2258,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))
     {
@@ -2278,6 +2284,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)
@@ -2320,7 +2377,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
@@ -2400,13 +2473,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. */
@@ -2418,8 +2484,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
        {
@@ -2506,6 +2575,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 ();
@@ -2517,12 +2601,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