(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
;; 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.
(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))))))
}
#endif /* HAVE_NS */
-static int
-item_width (const char *str)
+int
+menu_item_width (const char *str)
{
int len;
const char *p;
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;
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;
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;
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);
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);
#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;
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,
*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)
}
/* 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;
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
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)
{
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;
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;
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)
}
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: