]> git.eshelyaron.com Git - emacs.git/commitdiff
Horizontal keys in TTY menus work.
authorEli Zaretskii <eliz@gnu.org>
Tue, 8 Oct 2013 14:28:37 +0000 (17:28 +0300)
committerEli Zaretskii <eliz@gnu.org>
Tue, 8 Oct 2013 14:28:37 +0000 (17:28 +0300)
lisp/menu-bar.el
src/menu.c
src/menu.h
src/term.c

index 400c65e505be1d63b84ecb4a91d9514813cfce8f..71967e1474f3191b97c252cd416b50971dd6e1c1 100644 (file)
@@ -2197,8 +2197,14 @@ FROM-MENU-BAR, if non-nil, means we are dropping one of menu-bar's menus."
                         (filter (when (symbolp map)
                                   (plist-get (get map 'menu-prop) :filter))))
                    (if filter (funcall filter (symbol-function map)) map)))))
-        event cmd
-        (position (popup-menu-normalize-position position)))
+        (frame (selected-frame))
+        event cmd)
+    (if from-menu-bar
+       (let* ((xy (posn-x-y position))
+              (menu-symbol (menu-bar-menu-at-x-y (car xy) (cdr xy))))
+         (setq position (list menu-symbol (list frame '(menu-bar)
+                                                xy 0))))
+      (setq position (popup-menu-normalize-position position)))
     ;; The looping behavior was taken from lmenu's popup-menu-popup
     (while (and map (setq event
                          ;; map could be a prefix key, in which case
@@ -2209,19 +2215,36 @@ FROM-MENU-BAR, if non-nil, means we are dropping one of menu-bar's menus."
       ;; mouse-major-mode-menu was using a weird:
       ;; (key-binding (apply 'vector (append '(menu-bar) menu-prefix events)))
       (setq cmd
-           (if (and (not (keymapp map)) (listp map))
-               ;; We were given a list of keymaps.  Search them all
-               ;; in sequence until a first binding is found.
-               (let ((mouse-click (apply 'vector event))
-                     binding)
-                 (while (and map (null binding))
-                   (setq binding (lookup-key (car map) mouse-click))
-                   (if (numberp binding)       ; `too long'
-                       (setq binding nil))
-                   (setq map (cdr map)))
-                 binding)
+           (cond
+            ((and from-menu-bar
+                  (consp event)
+                  (numberp (car event))
+                  (numberp (cdr event)))
+             (let ((x (car event))
+                   (y (cdr event))
+                   menu-symbol)
+               (setq menu-symbol (menu-bar-menu-at-x-y x y))
+               (setq position (list menu-symbol (list frame '(menu-bar)
+                                                event 0)))
+               (setq map
+                     (or
+                      (lookup-key global-map (vector 'menu-bar menu-symbol))
+                      (lookup-key (current-local-map) (vector 'menu-bar
+                                                              menu-symbol))))))
+            ((and (not (keymapp map)) (listp map))
+             ;; We were given a list of keymaps.  Search them all
+             ;; in sequence until a first binding is found.
+             (let ((mouse-click (apply 'vector event))
+                   binding)
+               (while (and map (null binding))
+                 (setq binding (lookup-key (car map) mouse-click))
+                 (if (numberp binding) ; `too long'
+                     (setq binding nil))
+                 (setq map (cdr map)))
+                 binding))
+            (t
              ;; We were given a single keymap.
-             (lookup-key map (apply 'vector event))))
+             (lookup-key map (apply 'vector event)))))
       ;; Clear out echoing, which perhaps shows a prefix arg.
       (message "")
       ;; Maybe try again but with the submap.
@@ -2379,7 +2402,7 @@ If FRAME is nil or not given, use the selected frame."
        (popup-menu (or
                     (lookup-key global-map (vector 'menu-bar menu))
                     (lookup-key (current-local-map) (vector 'menu-bar menu)))
-                   (posn-at-x-y x 0 nil t) t)))
+                   (posn-at-x-y x 0 nil t) nil t)))
      (t (with-selected-frame (or frame (selected-frame))
           (tmm-menubar))))))
 
index 2c787e00b6f3d66a7c9a5d8f42f85ebb86408666..f741d686cd1eec37d8dcc40352eb4c7083a7995b 100644 (file)
@@ -1036,8 +1036,8 @@ find_and_return_menu_selection (struct frame *f, bool keymaps, void *client_data
 }
 #endif  /* HAVE_NS */
 
-static int
-item_width (const char *str)
+int
+menu_item_width (const char *str)
 {
   int len;
   const char *p;
@@ -1104,7 +1104,7 @@ into menu items.  */)
          if (XINT (pos) <= col
              /* We use <= so the blank between 2 items on a TTY is
                 considered part of the previous item.  */
-             && col <= XINT (pos) + item_width (SSDATA (str)))
+             && col <= XINT (pos) + menu_item_width (SSDATA (str)))
            {
              item = AREF (items, i);
              return item;
@@ -1160,7 +1160,7 @@ event (indicating that the user invoked the menu with the mouse) then
 no quit occurs and `x-popup-menu' returns nil.  */)
   (Lisp_Object position, Lisp_Object menu)
 {
-  Lisp_Object keymap, tem;
+  Lisp_Object keymap, tem, tem2;
   int xpos = 0, ypos = 0;
   Lisp_Object title;
   const char *error_name = NULL;
@@ -1169,6 +1169,7 @@ no quit occurs and `x-popup-menu' returns nil.  */)
   Lisp_Object x, y, window;
   bool keymaps = 0;
   bool for_click = 0;
+  bool kbd_menu_navigation = 0;
   ptrdiff_t specpdl_count = SPECPDL_INDEX ();
   struct gcpro gcpro1;
 
@@ -1202,6 +1203,22 @@ no quit occurs and `x-popup-menu' returns nil.  */)
            for_click = 1;
            tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
            window = Fcar (tem);             /* POSN_WINDOW (tem) */
+           tem2 = Fcar (Fcdr (tem));        /* POSN_POSN (tem) */
+           /* The kbd_menu_navigation flag is set when the menu was
+              invoked by F10, which probably means they have no
+              mouse.  In that case, we let them switch between
+              top-level menu-bar menus by using C-f/C-b and
+              horizontal arrow keys, since they cannot click the
+              mouse to open a different submenu.  This flag is only
+              supported by tty_menu_show.  We set it when POSITION
+              and last_nonmenu_event are different, which means we
+              constructed POSITION by hand (in popup-menu, see
+              menu-bar.el) to look like a mouse click on the menu bar
+              event.  */
+           if (!EQ (POSN_POSN (last_nonmenu_event),
+                    POSN_POSN (position))
+               && CONSP (tem2) && EQ (Fcar (tem2), Qmenu_bar))
+             kbd_menu_navigation = 1;
            tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
            x = Fcar (tem);
            y = Fcdr (tem);
@@ -1434,8 +1451,8 @@ no quit occurs and `x-popup-menu' returns nil.  */)
   else
 #endif
   if (FRAME_TERMCAP_P (f))
-    selection = tty_menu_show (f, xpos, ypos, for_click,
-                              keymaps, title, &error_name);
+    selection = tty_menu_show (f, xpos, ypos, for_click, keymaps, title,
+                              kbd_menu_navigation, &error_name);
 
 #ifdef HAVE_NS
   unbind_to (specpdl_count, Qnil);
index cdc1838ff9fade26c91e434e5bcb2ec95aca6c88..9b3b71d757eb55554e9ee56dd2576bca393d0107 100644 (file)
@@ -52,5 +52,6 @@ extern Lisp_Object ns_menu_show (struct frame *, int, int, bool, bool,
 extern Lisp_Object xmenu_show (struct frame *, int, int, bool, bool,
                               Lisp_Object, const char **, Time);
 extern Lisp_Object tty_menu_show (struct frame *, int, int, int, int,
-                                 Lisp_Object, const char **);
+                                 Lisp_Object, int, const char **);
+extern int menu_item_width (const char *);
 #endif /* MENU_H */
index d5c5f6ed7964b57a6d38f2cdebca6d0bbbcd6744..8515edf88a559f99ab7b6529b2b5381f0e6884a1 100644 (file)
@@ -2789,6 +2789,8 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop,
 #define TTYM_SUCCESS 1
 #define TTYM_NO_SELECT 2
 #define TTYM_IA_SELECT 3
+#define TTYM_NEXT 4
+#define TTYM_PREV 5
 
 /* These hold text of the current and the previous menu help messages.  */
 static const char *menu_help_message, *prev_menu_help_message;
@@ -3174,7 +3176,8 @@ screen_update (struct frame *f, struct glyph_matrix *mtx)
    puts us.  We only consider mouse movement and click events and
    keyboard movement commands; the rest are ignored.
 
-   Value is -1 if C-g was pressed, 1 if an item was selected, zero
+   Value is -1 if C-g was pressed, 1 if an item was selected, 2 or 3
+   if we need to move to the next or previous menu-bar menu, zero
    otherwise.  */
 static int
 read_menu_input (struct frame *sf, int *x, int *y, int min_y, int max_y,
@@ -3219,9 +3222,15 @@ read_menu_input (struct frame *sf, int *x, int *y, int min_y, int max_y,
          *y = my;
        }
       else if (EQ (cmd, Qtty_menu_next_menu))
-       *x += 1;
+       {
+         usable_input = 0;
+         st = 2;
+       }
       else if (EQ (cmd, Qtty_menu_prev_menu))
-       *x -= 1;
+       {
+         usable_input = 0;
+         st = 3;
+       }
       else if (EQ (cmd, Qtty_menu_next_item))
        {
          if (*y < max_y)
@@ -3255,10 +3264,11 @@ read_menu_input (struct frame *sf, int *x, int *y, int min_y, int max_y,
 }
 
 /* Display menu, wait for user's response, and return that response.  */
-int
+static int
 tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
                   int x0, int y0, char **txt,
-                  void (*help_callback)(char const *, int, int))
+                  void (*help_callback)(char const *, int, int),
+                  int kbd_navigation)
 {
   struct tty_menu_state *state;
   int statecount, x, y, i, b, leave, result, onepane;
@@ -3353,6 +3363,7 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
       input_status = read_menu_input (sf, &x, &y, min_y, max_y, &first_time);
       if (input_status)
        {
+         leave = 1;
          if (input_status == -1)
            {
              /* Remove the last help-echo, so that it doesn't
@@ -3360,7 +3371,20 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
              show_help_echo (Qnil, Qnil, Qnil, Qnil);
              result = TTYM_NO_SELECT;
            }
-         leave = 1;
+         else if (input_status == 2)
+           {
+             if (kbd_navigation)
+               result = TTYM_NEXT;
+             else
+               leave = 0;
+           }
+         else if (input_status == 3)
+           {
+             if (kbd_navigation)
+               result = TTYM_PREV;
+             else
+               leave = 0;
+           }
        }
       if (sf->mouse_moved && input_status != -1)
        {
@@ -3509,15 +3533,97 @@ tty_pop_down_menu (Lisp_Object arg)
   unblock_input ();
 }
 
+/* Return the zero-based index of the last menu-bar item on frame F.  */
+static int
+tty_menu_last_menubar_item (struct frame *f)
+{
+  int i = 0;
+
+  eassert (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f));
+  if (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f))
+    {
+      Lisp_Object items = FRAME_MENU_BAR_ITEMS (f);
+
+      while (i < ASIZE (items))
+       {
+         Lisp_Object str;
+
+         str = AREF (items, i + 1);
+         if (NILP (str))
+           break;
+         i += 4;
+       }
+      i -= 4;  /* went one too far */
+    }
+  return i;
+}
+
+/* Find in frame F's menu bar the menu item that is next or previous
+   to the item at X/Y, and return that item's position in X/Y.  WHICH
+   says which one--next or previous--item to look for.  X and Y are
+   measured in character cells.  This should only be called on TTY
+   frames.  */
+static void
+tty_menu_new_item_coords (struct frame *f, int which, int *x, int *y)
+{
+  eassert (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f));
+  if (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f))
+    {
+      Lisp_Object items = FRAME_MENU_BAR_ITEMS (f);
+      int last_i = tty_menu_last_menubar_item (f);
+      int i, prev_x;
+
+      /* This loop assumes a single menu-bar line, and will fail to
+        find an item if it is not in the first line.  Note that
+        make_lispy_event in keyboard.c makes the same assumption.  */
+      for (i = 0, prev_x = -1; i < ASIZE (items); i += 4)
+       {
+         Lisp_Object pos, str;
+         int ix;
+
+         str = AREF (items, i + 1);
+         pos = AREF (items, i + 3);
+         if (NILP (str))
+           return;
+         ix = XINT (pos);
+         if (ix <= *x
+             /* We use <= so the blank between 2 items on a TTY is
+                considered part of the previous item.  */
+             && *x <= ix + menu_item_width (SSDATA (str)))
+           {
+             /* Found current item.  Now compute the X coordinate of
+                the previous or next item.  */
+             if (which == TTYM_NEXT)
+               {
+                 if (i < last_i)
+                   *x = XINT (AREF (items, i + 4 + 3));
+                 else
+                   *x = 0;     /* wrap around to the first item */
+               }
+             else if (prev_x < 0)
+               {
+                 /* Wrap around to the last item.  */
+                 *x = XINT (AREF (items, last_i + 3));
+               }
+             else
+               *x = prev_x;
+             return;
+           }
+         prev_x = ix;
+       }
+    }
+}
+
 Lisp_Object
 tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
-              Lisp_Object title, const char **error_name)
+              Lisp_Object title, int kbd_navigation, const char **error_name)
 {
   tty_menu *menu;
   int pane, selidx, lpane, status;
   Lisp_Object entry, pane_prefix;
   char *datap;
   int ulx, uly, width, height;
+  int item_x, item_y;
   int dispwidth, dispheight;
   int i, j, lines, maxlines;
   int maxwidth;
@@ -3551,8 +3657,8 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
   inhibit_garbage_collection ();
 
   /* Adjust coordinates to be root-window-relative.  */
-  x += f->left_pos;
-  y += f->top_pos;
+  item_x = x += f->left_pos;
+  item_y = y += f->top_pos;
 
   /* Create all the necessary panes and their items.  */
   maxwidth = maxlines = lines = i = 0;
@@ -3710,7 +3816,7 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
   specbind (Qoverriding_terminal_local_map,
            Fsymbol_value (Qtty_menu_navigation_map));
   status = tty_menu_activate (menu, &pane, &selidx, x, y, &datap,
-                             tty_menu_help_callback);
+                             tty_menu_help_callback, kbd_navigation);
   entry = pane_prefix = Qnil;
 
   switch (status)
@@ -3751,6 +3857,12 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
        }
       break;
 
+    case TTYM_NEXT:
+    case TTYM_PREV:
+      tty_menu_new_item_coords (f, status, &item_x, &item_y);
+      entry = Fcons (make_number (item_x), make_number (item_y));
+      break;
+
     case TTYM_FAILURE:
       *error_name = "Can't activate menu";
     case TTYM_IA_SELECT: