(menubar_selection_callback, dialog_selection_callback)
[bpt/emacs.git] / src / xmenu.c
index fffcdc4..fd7c9a3 100644 (file)
@@ -42,6 +42,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "blockinput.h"
 #include "puresize.h"
 
+#ifdef MSDOS
+#include "msdos.h"
+#endif
+
 #ifdef HAVE_X_WINDOWS
 /* This may include sys/types.h, and that somehow loses
    if this is not done before the other system files.  */
@@ -57,9 +61,6 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "dispextern.h"
 
 #ifdef HAVE_X_WINDOWS
-#include "../oldXMenu/XMenu.h"
-#endif
-
 #ifdef USE_X_TOOLKIT
 #include <X11/Xlib.h>
 #include <X11/IntrinsicP.h>
@@ -67,7 +68,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <X11/StringDefs.h>
 #include <X11/Shell.h>
 #include "../lwlib/lwlib.h"
-#endif /* USE_X_TOOLKIT */
+#else /* not USE_X_TOOLKIT */
+#include "../oldXMenu/XMenu.h"
+#endif /* not USE_X_TOOLKIT */
+#endif /* HAVE_X_WINDOWS */
 
 #define min(x,y) (((x) < (y)) ? (x) : (y))
 #define max(x,y) (((x) > (y)) ? (x) : (y))
@@ -102,9 +106,9 @@ static void list_of_items ();
 
    Each pane is described by 3 elements in the vector:
    t, the pane name, the pane's prefix key.
-   Then follow the pane's items, with 4 elements per item:
+   Then follow the pane's items, with 5 elements per item:
    the item string, the enable flag, the item's value,
-   and the equivalent keyboard key's description string.
+   the definition, and the equivalent keyboard key's description string.
 
    In some cases, multiple levels of menus may be described.
    A single vector slot containing nil indicates the start of a submenu.
@@ -125,7 +129,8 @@ static void list_of_items ();
 #define MENU_ITEMS_ITEM_ENABLE 1
 #define MENU_ITEMS_ITEM_VALUE 2
 #define MENU_ITEMS_ITEM_EQUIV_KEY 3
-#define MENU_ITEMS_ITEM_LENGTH 4
+#define MENU_ITEMS_ITEM_DEFINITION 4
+#define MENU_ITEMS_ITEM_LENGTH 5
 
 static Lisp_Object menu_items;
 
@@ -146,6 +151,47 @@ static int menu_items_submenu_depth;
    Xt on behalf of one of the widget sets.  */
 static int popup_activated_flag;
 
+/* This holds a Lisp vector
+   which contains frames that have menu bars.
+   Each frame that has a menu bar is found at some index in this vector
+   and the menu bar widget refers to the frame through that index.  */
+static Lisp_Object frame_vector;
+\f
+/* Return the index of FRAME in frame_vector.
+   If FRAME isn't in frame_vector yet, put it in,
+   lengthening the vector if necessary.  */
+
+static int
+frame_vector_add_frame (f)
+     FRAME_PTR *f;
+{
+  int length = XVECTOR (frame_vector)->size;
+  int i, empty = -1;
+  Lisp_Object new, frame;
+
+  XSETFRAME (frame, f);
+
+  for (i = 0; i < length; i++)
+    {
+      if (EQ (frame, XVECTOR (frame_vector)->contents[i]))
+       return i;
+      if (NILP (XVECTOR (frame_vector)->contents[i]))
+       empty = i;
+    }
+
+  if (empty >= 0)
+    {
+      XVECTOR (frame_vector)->contents[empty] = frame;
+      return empty;
+    }
+
+  new = Fmake_vector (make_number (length * 2), Qnil);
+  bcopy (XVECTOR (frame_vector)->contents,
+        XVECTOR (new)->contents, sizeof (Lisp_Object) * length);
+  
+  XVECTOR (frame_vector)->contents[length] = frame;
+  return length;
+}
 \f
 /* Initialize the menu_items structure if we haven't already done so.
    Also mark it as currently empty.  */
@@ -257,12 +303,14 @@ push_menu_pane (name, prefix_vec)
 /* Push one menu item into the current pane.
    NAME is the string to display.  ENABLE if non-nil means
    this item can be selected.  KEY is the key generated by
-   choosing this item.  EQUIV is the textual description
-   of the keyboard equivalent for this item (or nil if none).  */
+   choosing this item, or nil if this item doesn't really have a definition.
+   DEF is the definition of this item.
+   EQUIV is the textual description of the keyboard equivalent for
+   this item (or nil if none).  */
 
 static void
-push_menu_item (name, enable, key, equiv)
-     Lisp_Object name, enable, key, equiv;
+push_menu_item (name, enable, key, def, equiv)
+     Lisp_Object name, enable, key, def, equiv;
 {
   if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
     grow_menu_items ();
@@ -271,6 +319,7 @@ push_menu_item (name, enable, key, equiv)
   XVECTOR (menu_items)->contents[menu_items_used++] = enable;
   XVECTOR (menu_items)->contents[menu_items_used++] = key;
   XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
+  XVECTOR (menu_items)->contents[menu_items_used++] = def;
 }
 \f
 /* Figure out the current keyboard equivalent of a menu item ITEM1.
@@ -336,29 +385,17 @@ menu_item_equiv_key (item_string, item1, descrip_ptr)
     {
       changed = 1;
       descrip = Qnil;
-      savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
       /* If the command is an alias for another
         (such as easymenu.el and lmenu.el set it up),
         see if the original command name has equivalent keys.  */
       if (SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function))
        savedkey = Fwhere_is_internal (XSYMBOL (def)->function,
                                       Qnil, Qt, Qnil);
+      else
+       /* Otherwise look up the specified command itself.
+          We don't try both, because that makes easymenu menus slow.  */
+       savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
 
-      if (VECTORP (savedkey)
-         && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar))
-       savedkey = Qnil;
-      /* Reject two-key sequences that start with a mouse click.
-        These are probably menu items.  */
-      if (VECTORP (savedkey)
-         && XVECTOR (savedkey)->size > 1
-         && SYMBOLP (XVECTOR (savedkey)->contents[0]))
-       {
-         Lisp_Object tem;
-
-         tem = Fget (XVECTOR (savedkey)->contents[0], Qevent_kind);
-         if (EQ (tem, Qmouse_click))
-           savedkey = Qnil;
-       }
       if (!NILP (savedkey))
        {
          descrip = Fkey_description (savedkey);
@@ -519,7 +556,9 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
                        item_string = concat2 (item_string,
                                               build_string (" >"));
 #endif
-                     push_menu_item (item_string, enabled, XCONS (item)->car,
+                     /* If definition is nil, pass nil as the key.  */
+                     push_menu_item (item_string, enabled,
+                                     XCONS (item)->car, def,
                                      descrip);
 #ifdef USE_X_TOOLKIT
                      /* Display a submenu using the toolkit.  */
@@ -588,8 +627,9 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
                            item_string = concat2 (item_string,
                                                   build_string (" >"));
 #endif
+                         /* If definition is nil, pass nil as the key.  */
                          push_menu_item (item_string, enabled, character,
-                                         descrip);
+                                         def, descrip);
 #ifdef USE_X_TOOLKIT
                          if (! NILP (submap))
                            {
@@ -660,7 +700,7 @@ list_of_items (pane)
     {
       item = Fcar (tail);
       if (STRINGP (item))
-       push_menu_item (item, Qnil, Qnil, Qnil);
+       push_menu_item (item, Qnil, Qnil, Qt, Qnil);
       else if (NILP (item))
        push_left_right_boundary ();
       else
@@ -668,7 +708,7 @@ list_of_items (pane)
          CHECK_CONS (item, 0);
          item1 = Fcar (item);
          CHECK_STRING (item1, 1);
-         push_menu_item (item1, Qt, Fcdr (item), Qnil);
+         push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil);
        }
     }
 }
@@ -716,6 +756,7 @@ cached information about equivalent key sequences.")
   Lisp_Object x, y, window;
   int keymaps = 0;
   int menubarp = 0;
+  int for_click = 0;
   struct gcpro gcpro1;
 
   if (! NILP (position))
@@ -726,13 +767,14 @@ cached information about equivalent key sequences.")
       if (EQ (position, Qt))
        {
          /* Use the mouse's current position.  */
-         FRAME_PTR new_f = 0;
+         FRAME_PTR new_f = selected_frame;
          Lisp_Object bar_window;
          int part;
          unsigned long time;
 
          if (mouse_position_hook)
-           (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+           (*mouse_position_hook) (&new_f, 1, &bar_window,
+                                   &part, &x, &y, &time);
          if (new_f != 0)
            XSETFRAME (window, new_f);
          else
@@ -753,6 +795,7 @@ cached information about equivalent key sequences.")
            }
          else
            {
+             for_click = 1;
              tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
              window = Fcar (tem);           /* POSN_WINDOW (tem) */
              tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
@@ -875,7 +918,7 @@ cached information about equivalent key sequences.")
   /* Display them in a menu.  */
   BLOCK_INPUT;
 
-  selection = xmenu_show (f, xpos, ypos, menubarp,
+  selection = xmenu_show (f, xpos, ypos, menubarp, for_click,
                          keymaps, title, &error_name);
   UNBLOCK_INPUT;
 
@@ -915,13 +958,13 @@ on the left of the dialog box and all following items on the right.\n\
     {
 #if 0 /* Using the frame the mouse is on may not be right.  */
       /* Use the mouse's current position.  */
-      FRAME_PTR new_f = 0;
+      FRAME_PTR new_f = selected_frame;
       Lisp_Object bar_window;
       int part;
       unsigned long time;
       Lisp_Object x, y;
 
-      (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+      (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
 
       if (new_f != 0)
        XSETFRAME (window, new_f);
@@ -1058,7 +1101,7 @@ menubar_selection_callback (widget, id, client_data)
      XtPointer client_data;
 {
   Lisp_Object prefix;
-  FRAME_PTR f = (FRAME_PTR) id;
+  FRAME_PTR f = XFRAME (XVECTOR (frame_vector)->contents[id]);
   Lisp_Object vector;
   Lisp_Object *subprefix_stack;
   int submenu_depth = 0;
@@ -1087,40 +1130,42 @@ menubar_selection_callback (widget, id, client_data)
        }
       else if (EQ (XVECTOR (vector)->contents[i], Qt))
        {
-         prefix
-           = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
+         prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
          i += MENU_ITEMS_PANE_LENGTH;
        }
       else
        {
-         entry
-           = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
-         if ((int) client_data == i)
+         entry = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
+         /* The EMACS_INT cast avoids a warning.  There's no problem
+            as long as pointers have enough bits to hold small integers.  */
+         if ((int) (EMACS_INT) client_data == i)
            {
              int j;
              struct input_event buf;
+             Lisp_Object frame;
 
+             XSETFRAME (frame, f);
              buf.kind = menu_bar_event;
-             buf.frame_or_window = Qmenu_bar;
+             buf.frame_or_window = Fcons (frame, Qmenu_bar);
              kbd_buffer_store_event (&buf);
 
              for (j = 0; j < submenu_depth; j++)
                if (!NILP (subprefix_stack[j]))
                  {
                    buf.kind = menu_bar_event;
-                   buf.frame_or_window = subprefix_stack[j];
+                   buf.frame_or_window = Fcons (frame, subprefix_stack[j]);
                    kbd_buffer_store_event (&buf);
                  }
 
              if (!NILP (prefix))
                {
                  buf.kind = menu_bar_event;
-                 buf.frame_or_window = prefix;
+                 buf.frame_or_window = Fcons (frame, prefix);
                  kbd_buffer_store_event (&buf);
                }
 
              buf.kind = menu_bar_event;
-             buf.frame_or_window = entry;
+             buf.frame_or_window = Fcons (frame, entry);
              kbd_buffer_store_event (&buf);
 
              return;
@@ -1283,11 +1328,12 @@ single_submenu (item_key, item_name, maps)
       else
        {
          /* Create a new item within current pane.  */
-         Lisp_Object item_name, enable, descrip;
+         Lisp_Object item_name, enable, descrip, def;
          item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
          enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
          descrip
            = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
+         def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
 
          wv = malloc_widget_value ();
          if (prev_wv) 
@@ -1298,7 +1344,9 @@ single_submenu (item_key, item_name, maps)
          if (!NILP (descrip))
            wv->key = (char *) XSTRING (descrip)->data;
          wv->value = 0;
-         wv->call_data = (void *) i;
+         /* The EMACS_INT cast avoids a warning.  There's no problem
+            as long as pointers have enough bits to hold small integers.  */
+         wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
          wv->enabled = !NILP (enable);
          prev_wv = wv;
 
@@ -1375,10 +1423,12 @@ set_frame_menubar (f, first_time)
      int first_time;
 {
   Widget menubar_widget = f->display.x->menubar_widget;
-  int id = (int) f;
-  Lisp_Object tail, items;
+  Lisp_Object tail, items, frame;
   widget_value *wv, *first_wv, *prev_wv = 0;
   int i;
+  int id;
+
+  id = frame_vector_add_frame (f);
 
   BLOCK_INPUT;
 
@@ -1490,12 +1540,13 @@ free_frame_menubar (f)
   int id;
 
   menubar_widget = f->display.x->menubar_widget;
-  id = (int) f;
   
   if (menubar_widget)
     {
+      id = frame_vector_add_frame (f);
       BLOCK_INPUT;
       lw_destroy_all_widgets (id);
+      XVECTOR (frame_vector)->contents[id] = Qnil;
       UNBLOCK_INPUT;
     }
 }
@@ -1510,7 +1561,8 @@ free_frame_menubar (f)
 /* F is the frame the menu is for.
    X and Y are the frame-relative specified position,
    relative to the inside upper left corner of the frame F.
-   MENUBARP is 1 if the click that asked for this menu came from the menu bar.
+   MENUBARP is 1 if this menu came from the menu bar.
+   FOR_CLICK if this menu was invoked for a mouse click.
    KEYMAPS is 1 if this menu was specified with keymaps;
     in that case, we return a list containing the chosen item's value
     and perhaps also the pane's prefix.
@@ -1541,12 +1593,12 @@ popup_selection_callback (widget, id, client_data)
 }
 
 static Lisp_Object
-xmenu_show (f, x, y, menubarp, keymaps, title, error)
+xmenu_show (f, x, y, menubarp, for_click, keymaps, title, error)
      FRAME_PTR f;
      int x;
      int y;
-     int menubarp;             /* Dummy parameter for Xt version of
-                                  xmenu_show() */
+     int menubarp;             /* This arg is unused in Xt version.  */
+     int for_click;
      int keymaps;
      Lisp_Object title;
      char **error;
@@ -1665,11 +1717,12 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       else
        {
          /* Create a new item within current pane.  */
-         Lisp_Object item_name, enable, descrip;
+         Lisp_Object item_name, enable, descrip, def;
          item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
          enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
          descrip
            = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
+         def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
 
          wv = malloc_widget_value ();
          if (prev_wv) 
@@ -1680,7 +1733,11 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          if (!NILP (descrip))
            wv->key = (char *) XSTRING (descrip)->data;
          wv->value = 0;
-         wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
+         /* If this item has a null value,
+            make the call_data null so that it won't display a box
+            when the mouse is on it.  */
+         wv->call_data
+           = (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0);
          wv->enabled = !NILP (enable);
          prev_wv = wv;
 
@@ -1811,7 +1868,9 @@ dialog_selection_callback (widget, id, client_data)
      LWLIB_ID id;
      XtPointer client_data;
 {
-  if ((int)client_data != -1)
+  /* The EMACS_INT cast avoids a warning.  There's no problem
+     as long as pointers have enough bits to hold small integers.  */
+  if ((int) (EMACS_INT) client_data != -1)
     menu_item_selection = (Lisp_Object *) client_data;
   BLOCK_INPUT;
   lw_destroy_all_widgets (id);
@@ -2055,11 +2114,12 @@ xdialog_show (f, menubarp, keymaps, title, error)
 #else /* not USE_X_TOOLKIT */
 
 static Lisp_Object
-xmenu_show (f, x, y, menubarp, keymaps, title, error)
+xmenu_show (f, x, y, menubarp, for_click, keymaps, title, error)
      FRAME_PTR f;
      int x, y;
-     int keymaps;
      int menubarp;
+     int for_click;
+     int keymaps;
      Lisp_Object title;
      char **error;
 {
@@ -2263,6 +2323,12 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   
   status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
                          x, y, ButtonReleaseMask, &datap);
+
+
+  /* Assume the mouse has moved out of the X window.
+     If it has actually moved in, we will get an EnterNotify.  */
+  x_mouse_leave ();
+
   switch (status)
     {
     case XM_SUCCESS:
@@ -2336,6 +2402,9 @@ syms_of_xmenu ()
   widget_id_tick = (1<<16);    
 #endif
 
+  staticpro (&frame_vector);
+  frame_vector = Fmake_vector (make_number (10), Qnil);
+
   defsubr (&Sx_popup_menu);
   defsubr (&Sx_popup_dialog);
 }