1 /* Menu system implementation. */
4 #define _GNU_SOURCE /* XXX: we _WANT_ strcasestr() ! */
16 #include "bfu/hotkey.h"
17 #include "bfu/inpfield.h"
19 #include "config/kbdbind.h"
20 #include "intl/gettext/libintl.h"
21 #include "session/session.h"
22 #include "terminal/draw.h"
23 #include "terminal/event.h"
24 #include "terminal/kbd.h"
25 #include "terminal/mouse.h"
26 #include "terminal/tab.h"
27 #include "terminal/terminal.h"
28 #include "terminal/window.h"
29 #include "util/color.h"
30 #include "util/conv.h"
31 #include "util/memory.h"
32 #include "viewer/action.h"
34 /* Left and right main menu reserved spaces. */
35 #define L_MAINMENU_SPACE 2
36 #define R_MAINMENU_SPACE 2
38 /* Left and right padding spaces around labels in main menu. */
39 #define L_MAINTEXT_SPACE 1
40 #define R_MAINTEXT_SPACE 1
42 /* Spaces before and after right text of submenu. */
43 #define L_RTEXT_SPACE 1
44 #define R_RTEXT_SPACE 1
46 /* Spaces before and after left text of submenu. */
47 #define L_TEXT_SPACE 1
48 #define R_TEXT_SPACE 1
50 /* Border size in submenu. */
51 #define MENU_BORDER_SIZE 1
54 /* Types and structures */
56 /* Submenu indicator, displayed at right. */
57 static unsigned char m_submenu
[] = ">>";
58 static int m_submenu_len
= sizeof(m_submenu
) - 1;
61 static window_handler_T menu_handler
;
62 static window_handler_T mainmenu_handler
;
63 static void set_menu_selection(struct menu
*menu
, int pos
);
67 count_items(struct menu_item
*items
)
72 struct menu_item
*item
;
74 foreach_menu_item (item
, items
) {
83 free_menu_items(struct menu_item
*items
)
85 struct menu_item
*item
;
87 if (!items
|| !(items
->flags
& FREE_ANY
)) return;
89 /* Note that flags & FREE_DATA applies only when menu is aborted;
90 * it is zeroed when some menu field is selected. */
92 foreach_menu_item (item
, items
) {
93 if (item
->flags
& FREE_TEXT
) mem_free_if(item
->text
);
94 if (item
->flags
& FREE_RTEXT
) mem_free_if(item
->rtext
);
95 if (item
->flags
& FREE_DATA
) mem_free_if(item
->data
);
102 do_menu_selected(struct terminal
*term
, struct menu_item
*items
,
103 void *data
, int selected
, int hotkeys
)
105 struct menu
*menu
= mem_calloc(1, sizeof(*menu
));
108 menu
->selected
= selected
;
111 menu
->size
= count_items(items
);
112 menu
->hotkeys
= hotkeys
;
116 refresh_hotkeys(term
, menu
);
117 add_window(term
, menu_handler
, menu
);
119 free_menu_items(items
);
124 do_menu(struct terminal
*term
, struct menu_item
*items
, void *data
, int hotkeys
)
126 do_menu_selected(term
, items
, data
, 0, hotkeys
);
130 select_menu_item(struct terminal
*term
, struct menu_item
*it
, void *data
)
132 /* We save these values due to delete_window() call below. */
133 menu_func_T func
= it
->func
;
134 void *it_data
= it
->data
;
135 enum main_action action_id
= it
->action_id
;
137 if (!mi_is_selectable(it
)) return;
139 if (!mi_is_submenu(it
)) {
140 /* Don't free data! */
141 it
->flags
&= ~FREE_DATA
;
143 while (!list_empty(term
->windows
)) {
144 struct window
*win
= term
->windows
.next
;
146 if (win
->handler
!= menu_handler
147 && win
->handler
!= mainmenu_handler
)
154 if (action_id
!= ACT_MAIN_NONE
&& !func
) {
155 struct session
*ses
= data
;
157 do_action(ses
, action_id
, 1);
161 assertm(func
, "No menu function");
162 if_assert_failed
return;
164 func(term
, it_data
, data
);
168 select_menu(struct terminal
*term
, struct menu
*menu
)
170 if (menu
->selected
< 0 || menu
->selected
>= menu
->size
)
173 select_menu_item(term
, &menu
->items
[menu
->selected
], menu
->data
);
176 /* Get desired width for left text in menu item, accounting spacing. */
178 get_menuitem_text_width(struct terminal
*term
, struct menu_item
*mi
)
182 if (!mi_has_left_text(mi
)) return 0;
185 if (mi_text_translate(mi
))
186 text
= _(text
, term
);
188 if (!text
[0]) return 0;
190 return L_TEXT_SPACE
+ strlen(text
) - !!mi
->hotkey_pos
+ R_TEXT_SPACE
;
193 /* Get desired width for right text in menu item, accounting spacing. */
195 get_menuitem_rtext_width(struct terminal
*term
, struct menu_item
*mi
)
199 if (mi_is_submenu(mi
)) {
200 rtext_width
= L_RTEXT_SPACE
+ m_submenu_len
+ R_RTEXT_SPACE
;
202 } else if (mi
->action_id
!= ACT_MAIN_NONE
) {
203 struct string keystroke
;
205 if (init_string(&keystroke
)) {
206 add_keystroke_action_to_string(&keystroke
, mi
->action_id
, KEYMAP_MAIN
);
207 rtext_width
= L_RTEXT_SPACE
+ keystroke
.length
+ R_RTEXT_SPACE
;
208 done_string(&keystroke
);
211 } else if (mi_has_right_text(mi
)) {
212 unsigned char *rtext
= mi
->rtext
;
214 if (mi_rtext_translate(mi
))
215 rtext
= _(rtext
, term
);
218 rtext_width
= L_RTEXT_SPACE
+ strlen(rtext
) + R_RTEXT_SPACE
;
225 get_menuitem_width(struct terminal
*term
, struct menu_item
*mi
, int max_width
)
227 int text_width
= get_menuitem_text_width(term
, mi
);
228 int rtext_width
= get_menuitem_rtext_width(term
, mi
);
230 int_upper_bound(&text_width
, max_width
);
231 int_upper_bound(&rtext_width
, max_width
- text_width
);
233 return text_width
+ rtext_width
;
237 count_menu_size(struct terminal
*term
, struct menu
*menu
)
239 struct menu_item
*item
;
240 int width
= term
->width
- MENU_BORDER_SIZE
* 2;
241 int height
= term
->height
- MENU_BORDER_SIZE
* 2;
242 int my
= int_min(menu
->size
, height
);
245 foreach_menu_item (item
, menu
->items
) {
246 int_lower_bound(&mx
, get_menuitem_width(term
, item
, width
));
250 menu
->parent_x
, menu
->parent_y
,
251 mx
+ MENU_BORDER_SIZE
* 2,
252 my
+ MENU_BORDER_SIZE
* 2);
254 int_bounds(&menu
->box
.x
, 0, width
- mx
);
255 int_bounds(&menu
->box
.y
, 0, height
- my
);
259 scroll_menu(struct menu
*menu
, int steps
, int wrap
)
262 int s
= steps
? steps
/abs(steps
) : 1; /* Selectable item search direction. */
264 /* menu->selected sometimes became -2 and caused infinite loops.
265 * That should no longer be possible. */
266 assert(menu
->selected
>= -1);
267 if_assert_failed
return;
269 if (menu
->size
<= 0) {
277 start
= pos
= menu
->selected
;
280 /* The caller wants us to check that menu->selected is
281 * actually selectable, and correct it if not. */
288 pos
+= s
, steps
-= s
;
293 } else if (pos
>= menu
->size
&& s
== 1) {
297 pos
= menu
->size
- 1;
300 } else if (pos
< 0 && s
== -1) {
302 pos
= menu
->size
- 1;
307 } else if (!mi_is_selectable(&menu
->items
[pos
])) {
313 if (start
== -1) start
= 0;
318 if (!mi_is_selectable(&menu
->items
[pos
]))
320 set_menu_selection(menu
, pos
);
323 /* Set menu->selected = pos, and adjust menu->first if needed.
324 * This neither redraws the menu nor runs the item's action.
325 * The caller must ensure that menu->items[pos] is selectable. */
327 set_menu_selection(struct menu
*menu
, int pos
)
331 assert(pos
>= 0 && pos
< menu
->size
);
332 assert(mi_is_selectable(&menu
->items
[pos
]));
333 if_assert_failed
return;
335 menu
->selected
= pos
;
337 height
= int_max(1, menu
->box
.height
- MENU_BORDER_SIZE
* 2);
339 /* The rest is not needed for horizontal menus like the mainmenu.
340 * FIXME: We need a better way to figure out which menus are horizontal and
341 * which are vertical (normal) --jonas */
342 if (height
== 1) return;
344 scr_i
= int_min((height
- 1) / 2, SCROLL_ITEMS
);
346 int_bounds(&menu
->first
, menu
->selected
- height
+ scr_i
+ 1, menu
->selected
- scr_i
);
347 int_bounds(&menu
->first
, 0, menu
->size
- height
);
351 draw_menu_left_text(struct terminal
*term
, unsigned char *text
, int len
,
352 int x
, int y
, int width
, struct color_pair
*color
)
354 int w
= width
- (L_TEXT_SPACE
+ R_TEXT_SPACE
);
358 if (len
< 0) len
= strlen(text
);
360 if (len
> w
) len
= w
;
362 draw_text(term
, x
+ L_TEXT_SPACE
, y
, text
, len
, 0, color
);
367 draw_menu_left_text_hk(struct terminal
*term
, unsigned char *text
,
368 int hotkey_pos
, int x
, int y
, int width
,
369 struct color_pair
*color
, int selected
)
371 struct color_pair
*hk_color
= get_bfu_color(term
, "menu.hotkey.normal");
372 struct color_pair
*hk_color_sel
= get_bfu_color(term
, "menu.hotkey.selected");
373 enum screen_char_attr hk_attr
= get_opt_bool("ui.dialogs.underline_hotkeys")
374 ? SCREEN_ATTR_UNDERLINE
: 0;
376 int xbase
= x
+ L_TEXT_SPACE
;
377 int w
= width
- (L_TEXT_SPACE
+ R_TEXT_SPACE
);
380 /* For redundant hotkeys highlighting. */
383 if (hotkey_pos
< 0) hotkey_pos
= -hotkey_pos
, double_hk
= 1;
386 if (!hotkey_pos
|| w
<= 0) return;
389 struct color_pair
*tmp
= hk_color
;
391 hk_color
= hk_color_sel
;
395 for (x
= 0; x
- !!hk_state
< w
&& (c
= text
[x
]); x
++) {
396 if (!hk_state
&& x
== hotkey_pos
- 1) {
403 draw_char(term
, xbase
+ x
- 1, y
, c
, hk_attr
,
404 (double_hk
? hk_color_sel
: hk_color
));
406 draw_char(term
, xbase
+ x
- 1, y
, c
, hk_attr
, hk_color
);
410 draw_char(term
, xbase
+ x
- !!hk_state
, y
, c
, 0, color
);
416 draw_menu_right_text(struct terminal
*term
, unsigned char *text
, int len
,
417 int x
, int y
, int width
, struct color_pair
*color
)
419 int w
= width
- (L_RTEXT_SPACE
+ R_RTEXT_SPACE
);
423 if (len
< 0) len
= strlen(text
);
425 if (len
> w
) len
= w
;
427 x
+= w
- len
+ L_RTEXT_SPACE
+ L_TEXT_SPACE
;
429 draw_text(term
, x
, y
, text
, len
, 0, color
);
433 display_menu(struct terminal
*term
, struct menu
*menu
)
435 struct color_pair
*normal_color
= get_bfu_color(term
, "menu.normal");
436 struct color_pair
*selected_color
= get_bfu_color(term
, "menu.selected");
437 struct color_pair
*frame_color
= get_bfu_color(term
, "menu.frame");
443 menu
->box
.x
+ MENU_BORDER_SIZE
,
444 menu
->box
.y
+ MENU_BORDER_SIZE
,
445 int_max(0, menu
->box
.width
- MENU_BORDER_SIZE
* 2),
446 int_max(0, menu
->box
.height
- MENU_BORDER_SIZE
* 2));
448 draw_box(term
, &box
, ' ', 0, normal_color
);
449 draw_border(term
, &box
, frame_color
, 1);
451 if (get_opt_bool("ui.dialogs.shadows")) {
453 draw_shadow(term
, &menu
->box
,
454 get_bfu_color(term
, "dialog.shadow"), 2, 1);
457 menu_height
= box
.height
;
460 for (p
= menu
->first
;
461 p
< menu
->size
&& p
< menu
->first
+ menu_height
;
463 struct color_pair
*color
= normal_color
;
464 struct menu_item
*mi
= &menu
->items
[p
];
465 int selected
= (p
== menu
->selected
);
469 if (mi_is_end_of_menu(mi
))
470 INTERNAL("Unexpected end of menu [%p:%d]", mi
, p
);
474 /* This entry is selected. */
475 color
= selected_color
;
477 set_cursor(term
, box
.x
, box
.y
, 1);
478 set_window_ptr(menu
->win
, menu
->box
.x
+ menu
->box
.width
, box
.y
);
479 draw_box(term
, &box
, ' ', 0, color
);
482 if (mi_is_horizontal_bar(mi
)) {
483 /* Horizontal separator */
484 draw_border_char(term
, menu
->box
.x
, box
.y
,
485 BORDER_SRTEE
, frame_color
);
487 draw_box(term
, &box
, BORDER_SHLINE
,
488 SCREEN_ATTR_FRAME
, frame_color
);
490 draw_border_char(term
, box
.x
+ box
.width
, box
.y
,
491 BORDER_SLTEE
, frame_color
);
496 if (mi_has_left_text(mi
)) {
497 int l
= mi
->hotkey_pos
;
498 unsigned char *text
= mi
->text
;
500 if (mi_text_translate(mi
))
501 text
= _(text
, term
);
503 if (!mi_is_selectable(mi
))
507 draw_menu_left_text_hk(term
, text
, l
,
508 box
.x
, box
.y
, box
.width
, color
,
512 draw_menu_left_text(term
, text
, -1,
513 box
.x
, box
.y
, box
.width
, color
);
517 if (mi_is_submenu(mi
)) {
518 draw_menu_right_text(term
, m_submenu
, m_submenu_len
,
519 menu
->box
.x
, box
.y
, box
.width
, color
);
520 } else if (mi
->action_id
!= ACT_MAIN_NONE
) {
521 struct string keystroke
;
524 /* Help to detect action + right text. --Zas */
525 if (mi_has_right_text(mi
)) {
526 if (color
== selected_color
)
527 color
= normal_color
;
529 color
= selected_color
;
531 #endif /* CONFIG_DEBUG */
533 if (init_string(&keystroke
)) {
534 add_keystroke_action_to_string(&keystroke
,
537 draw_menu_right_text(term
, keystroke
.source
,
541 done_string(&keystroke
);
544 } else if (mi_has_right_text(mi
)) {
545 unsigned char *rtext
= mi
->rtext
;
547 if (mi_rtext_translate(mi
))
548 rtext
= _(rtext
, term
);
551 /* There's a right text, so print it */
552 draw_menu_right_text(term
, rtext
, -1,
554 box
.y
, box
.width
, color
);
559 redraw_from_window(menu
->win
);
565 menu_mouse_handler(struct menu
*menu
, struct term_event
*ev
)
567 struct window
*win
= menu
->win
;
568 int scroll_direction
= 1;
570 switch (get_mouse_button(ev
)) {
572 scroll_direction
= -1;
575 if (check_mouse_action(ev
, B_DOWN
)) {
576 scroll_menu(menu
, scroll_direction
, 1);
577 display_menu(win
->term
, menu
);
582 if (!check_mouse_position(ev
, &menu
->box
)) {
583 if (check_mouse_action(ev
, B_DOWN
)) {
584 delete_window_ev(win
, ev
);
588 struct window
*end
= (struct window
*) &win
->term
->windows
;
590 for (w1
= win
; w1
!= end
; w1
= w1
->next
) {
593 if (w1
->handler
== mainmenu_handler
) {
594 if (!ev
->info
.mouse
.y
)
595 delete_window_ev(win
, ev
);
599 if (w1
->handler
!= menu_handler
) break;
603 if (check_mouse_position(ev
, &m1
->box
)) {
604 delete_window_ev(win
, ev
);
611 int sel
= ev
->info
.mouse
.y
- menu
->box
.y
- 1 + menu
->first
;
613 if (sel
>= 0 && sel
< menu
->size
614 && mi_is_selectable(&menu
->items
[sel
])) {
615 set_menu_selection(menu
, sel
);
616 display_menu(win
->term
, menu
);
617 select_menu(win
->term
, menu
);
626 menu_page_up(struct menu
*menu
)
628 int current
= int_max(0, int_min(menu
->selected
, menu
->size
- 1));
633 for (i
= current
- 1; i
> 0; i
--)
634 if (mi_is_horizontal_bar(&menu
->items
[i
])) {
639 step
= current
- next_sep
+ 1;
640 int_bounds(&step
, 0, int_min(current
, DIST
));
642 scroll_menu(menu
, -step
, 0);
646 menu_page_down(struct menu
*menu
)
648 int current
= int_max(0, int_min(menu
->selected
, menu
->size
- 1));
651 int next_sep
= menu
->size
- 1;
653 for (i
= current
+ 1; i
< menu
->size
; i
++)
654 if (mi_is_horizontal_bar(&menu
->items
[i
])) {
659 step
= next_sep
- current
+ 1;
660 int_bounds(&step
, 0, int_min(menu
->size
- 1 - current
, DIST
));
662 scroll_menu(menu
, step
, 0);
668 search_menu_item(struct menu_item
*item
, unsigned char *buffer
,
669 struct terminal
*term
)
671 unsigned char *text
, *match
;
673 /* set_menu_selection asserts selectability. */
674 if (!mi_has_left_text(item
) || !mi_is_selectable(item
)) return 0;
676 text
= mi_text_translate(item
) ? _(item
->text
, term
) : item
->text
;
678 /* Crap. We have to remove the hotkey markers '~' */
679 text
= stracpy(text
);
682 match
= strchr(text
, '~');
684 memmove(match
, match
+ 1, strlen(match
));
686 match
= strcasestr(text
, buffer
);
692 static enum input_line_code
693 menu_search_handler(struct input_line
*line
, int action_id
)
695 struct menu
*menu
= line
->data
;
696 struct terminal
*term
= menu
->win
->term
;
697 unsigned char *buffer
= line
->buffer
;
699 int pos
= menu
->selected
;
704 case ACT_EDIT_REDRAW
:
705 return INPUT_LINE_PROCEED
;
708 /* XXX: The input line dialog window is above the menu window.
709 * Remove it from the top, so that select_menu() will correctly
710 * remove all the windows it has to and then readd it. */
711 win
= term
->windows
.next
;
713 select_menu(term
, menu
);
714 add_to_list(term
->windows
, win
);
715 return INPUT_LINE_CANCEL
;
717 case ACT_EDIT_PREVIOUS_ITEM
:
722 case ACT_EDIT_NEXT_ITEM
:
728 /* If there is nothing to match with don't start searching */
729 if (!*buffer
) return INPUT_LINE_PROCEED
;
735 struct menu_item
*item
= &menu
->items
[pos
];
737 if (search_menu_item(item
, buffer
, term
)) {
738 set_menu_selection(menu
, pos
);
739 display_menu(term
, menu
);
740 return INPUT_LINE_PROCEED
;
745 if (pos
== menu
->size
) pos
= 0;
746 else if (pos
< 0) pos
= menu
->size
- 1;
747 } while (pos
!= start
);
749 return INPUT_LINE_CANCEL
;
753 search_menu(struct menu
*menu
)
755 struct terminal
*term
= menu
->win
->term
;
756 struct window
*tab
= get_current_tab(term
);
757 struct session
*ses
= tab
? tab
->data
: NULL
;
758 unsigned char *prompt
= _("Search menu/", term
);
760 if (menu
->size
< 1 || !ses
) return;
762 input_field_line(ses
, prompt
, menu
, NULL
, menu_search_handler
);
766 menu_kbd_handler(struct menu
*menu
, struct term_event
*ev
)
768 struct window
*win
= menu
->win
;
769 enum menu_action action_id
= kbd_action(KEYMAP_MENU
, ev
, NULL
);
775 if (list_has_next(win
->term
->windows
, win
)
776 && win
->next
->handler
== mainmenu_handler
) {
777 struct window
*next_win
= win
->next
;
779 delete_window_ev(win
, ev
);
781 select_menu(next_win
->term
, next_win
->data
);
786 if (action_id
== ACT_MENU_RIGHT
)
793 scroll_menu(menu
, -1, 1);
797 scroll_menu(menu
, 1, 1);
801 scroll_menu(menu
, -menu
->selected
, 0);
805 scroll_menu(menu
, menu
->size
- menu
->selected
- 1, 0);
808 case ACT_MENU_PAGE_UP
:
812 case ACT_MENU_PAGE_DOWN
:
813 menu_page_down(menu
);
817 case ACT_MENU_SELECT
:
820 case ACT_MENU_SEARCH
:
824 case ACT_MENU_CANCEL
:
825 if (list_has_next(win
->term
->windows
, win
)
826 && win
->next
->handler
== mainmenu_handler
)
827 delete_window_ev(win
, ev
);
829 delete_window_ev(win
, NULL
);
835 int key
= get_kbd_key(ev
);
837 if ((key
>= KBD_F1
&& key
<= KBD_F12
)
838 || check_kbd_modifier(ev
, KBD_MOD_ALT
)) {
839 delete_window_ev(win
, ev
);
843 if (!check_kbd_label_key(ev
))
846 s
= check_hotkeys(menu
, key
, win
->term
);
848 if (s
|| check_not_so_hot_keys(menu
, key
, win
->term
))
849 scroll_menu(menu
, 0, 1);
853 display_menu(win
->term
, menu
);
856 select_menu(win
->term
, menu
);
861 menu_handler(struct window
*win
, struct term_event
*ev
)
863 struct menu
*menu
= win
->data
;
870 get_parent_ptr(win
, &menu
->parent_x
, &menu
->parent_y
);
872 count_menu_size(win
->term
, menu
);
873 /* do_menu sets menu->selected = 0. If that
874 * item isn't actually selectable, correct
875 * menu->selected here. */
876 scroll_menu(menu
, 0, 1);
877 display_menu(win
->term
, menu
);
882 menu_mouse_handler(menu
, ev
);
883 #endif /* CONFIG_MOUSE */
887 menu_kbd_handler(menu
, ev
);
891 free_menu_items(menu
->items
);
898 do_mainmenu(struct terminal
*term
, struct menu_item
*items
,
901 struct menu
*menu
= mem_calloc(1, sizeof(*menu
));
905 menu
->selected
= (sel
== -1 ? 0 : sel
);
908 menu
->size
= count_items(items
);
912 clear_hotkeys_cache(menu
);
914 init_hotkeys(term
, menu
);
915 add_window(term
, mainmenu_handler
, menu
);
918 select_menu(term
, menu
);
923 display_mainmenu(struct terminal
*term
, struct menu
*menu
)
925 struct color_pair
*normal_color
= get_bfu_color(term
, "menu.normal");
926 struct color_pair
*selected_color
= get_bfu_color(term
, "menu.selected");
931 /* FIXME: menu horizontal scrolling do not work well yet, we need to cache
932 * menu items width and recalculate them only when needed (ie. language change)
933 * instead of looping and calculate them each time. --Zas */
935 /* Try to make current selected menu entry visible. */
936 if (menu
->selected
< menu
->first
) {
937 int num_items_offscreen
= menu
->selected
- menu
->first
;
939 menu
->first
+= num_items_offscreen
;
940 menu
->last
+= num_items_offscreen
;
941 } else if (menu
->selected
> menu
->last
) {
942 int num_items_offscreen
= menu
->last
- menu
->selected
;
944 menu
->first
-= num_items_offscreen
;
945 menu
->last
-= num_items_offscreen
;
949 menu
->last
= menu
->size
- 1;
951 int_bounds(&menu
->last
, 0, menu
->size
- 1);
952 int_bounds(&menu
->first
, 0, menu
->last
);
954 set_box(&box
, 0, 0, term
->width
, 1);
955 draw_box(term
, &box
, ' ', 0, normal_color
);
957 if (menu
->first
!= 0) {
958 box
.width
= L_MAINMENU_SPACE
;
959 draw_box(term
, &box
, '<', 0, normal_color
);
962 p
+= L_MAINMENU_SPACE
;
964 for (i
= menu
->first
; i
< menu
->size
; i
++) {
965 struct menu_item
*mi
= &menu
->items
[i
];
966 struct color_pair
*color
= normal_color
;
967 unsigned char *text
= mi
->text
;
968 int l
= mi
->hotkey_pos
;
970 int selected
= (i
== menu
->selected
);
972 if (mi_text_translate(mi
))
973 text
= _(text
, term
);
975 textlen
= strlen(text
) - !!l
;
978 color
= selected_color
;
980 box
.width
= L_MAINTEXT_SPACE
+ L_TEXT_SPACE
982 + R_TEXT_SPACE
+ R_MAINTEXT_SPACE
;
983 draw_box(term
, &box
, ' ', 0, color
);
984 set_cursor(term
, p
, 0, 1);
985 set_window_ptr(menu
->win
, p
, 1);
988 p
+= L_MAINTEXT_SPACE
;
991 draw_menu_left_text_hk(term
, text
, l
,
992 p
, 0, textlen
+ R_TEXT_SPACE
+ L_TEXT_SPACE
,
995 draw_menu_left_text(term
, text
, textlen
,
996 p
, 0, textlen
+ R_TEXT_SPACE
+ L_TEXT_SPACE
,
1002 if (p
>= term
->width
- R_MAINMENU_SPACE
)
1005 p
+= R_MAINTEXT_SPACE
+ R_TEXT_SPACE
+ L_TEXT_SPACE
;
1009 int_lower_bound(&menu
->last
, menu
->first
);
1010 if (menu
->last
< menu
->size
- 1) {
1012 term
->width
- R_MAINMENU_SPACE
, 0,
1013 R_MAINMENU_SPACE
, 1);
1014 draw_box(term
, &box
, '>', 0, normal_color
);
1017 redraw_from_window(menu
->win
);
1023 mainmenu_mouse_handler(struct menu
*menu
, struct term_event
*ev
)
1025 struct window
*win
= menu
->win
;
1026 struct menu_item
*item
;
1029 if (check_mouse_wheel(ev
))
1032 /* Mouse was clicked outside the mainmenu bar */
1033 if (ev
->info
.mouse
.y
) {
1034 if (check_mouse_action(ev
, B_DOWN
))
1035 delete_window_ev(win
, NULL
);
1040 /* First check if the mouse button was pressed in the side of the
1041 * terminal and simply scroll one step in that direction else iterate
1042 * through the menu items to see if it was pressed on a label. */
1043 if (ev
->info
.mouse
.x
< L_MAINMENU_SPACE
) {
1046 } else if (ev
->info
.mouse
.x
>= win
->term
->width
- R_MAINMENU_SPACE
) {
1050 int p
= L_MAINMENU_SPACE
;
1052 /* We don't initialize to menu->first here, since it breaks
1053 * horizontal scrolling using mouse in some cases. --Zas */
1054 foreach_menu_item (item
, menu
->items
) {
1055 unsigned char *text
= item
->text
;
1057 if (!mi_has_left_text(item
)) continue;
1059 if (mi_text_translate(item
))
1060 text
= _(item
->text
, win
->term
);
1062 /* The label width is made up of a little padding on
1063 * the sides followed by the text width substracting
1064 * one char if it has hotkeys (the '~' char) */
1065 p
+= L_MAINTEXT_SPACE
+ L_TEXT_SPACE
1066 + strlen(text
) - !!item
->hotkey_pos
1067 + R_TEXT_SPACE
+ R_MAINTEXT_SPACE
;
1069 if (ev
->info
.mouse
.x
< p
) {
1070 scroll
= (item
- menu
->items
) - menu
->selected
;
1077 scroll_menu(menu
, scroll
, 1);
1078 display_mainmenu(win
->term
, menu
);
1081 /* We need to select the menu item even if we didn't scroll
1082 * apparently because we will delete any drop down menus
1083 * in the clicking process. */
1084 select_menu(win
->term
, menu
);
1089 mainmenu_kbd_handler(struct menu
*menu
, struct term_event
*ev
)
1091 struct window
*win
= menu
->win
;
1092 enum menu_action action_id
= kbd_action(KEYMAP_MENU
, ev
, NULL
);
1094 switch (action_id
) {
1095 case ACT_MENU_ENTER
:
1098 case ACT_MENU_PAGE_UP
:
1099 case ACT_MENU_PAGE_DOWN
:
1100 case ACT_MENU_SELECT
:
1101 select_menu(win
->term
, menu
);
1105 scroll_menu(menu
, -menu
->selected
, 0);
1109 scroll_menu(menu
, menu
->size
- menu
->selected
- 1, 0);
1112 case ACT_MENU_NEXT_ITEM
:
1113 case ACT_MENU_PREVIOUS_ITEM
:
1114 /* This is pretty western centric since `what is next'?
1115 * Anyway we cycle clockwise by resetting the action ... */
1116 action_id
= (action_id
== ACT_MENU_NEXT_ITEM
)
1117 ? ACT_MENU_RIGHT
: ACT_MENU_LEFT
;
1118 /* ... and then letting left/right handling take over. */
1121 case ACT_MENU_RIGHT
:
1122 scroll_menu(menu
, action_id
== ACT_MENU_LEFT
? -1 : 1, 1);
1125 case ACT_MENU_REDRAW
:
1126 /* Just call display_mainmenu() */
1130 /* Fallback to see if any hotkey matches the pressed key */
1131 if (check_kbd_label_key(ev
)
1132 && check_hotkeys(menu
, get_kbd_key(ev
), win
->term
)) {
1133 display_mainmenu(win
->term
, menu
);
1134 select_menu(win
->term
, menu
);
1139 case ACT_MENU_CANCEL
:
1140 delete_window_ev(win
, action_id
!= ACT_MENU_CANCEL
? ev
: NULL
);
1144 /* Redraw the menu */
1145 display_mainmenu(win
->term
, menu
);
1149 mainmenu_handler(struct window
*win
, struct term_event
*ev
)
1151 struct menu
*menu
= win
->data
;
1159 display_mainmenu(win
->term
, menu
);
1164 mainmenu_mouse_handler(menu
, ev
);
1165 #endif /* CONFIG_MOUSE */
1169 mainmenu_kbd_handler(menu
, ev
);
1177 /* For dynamic menus the last (cleared) item is used to mark the end. */
1178 #define realloc_menu_items(mi_, size) \
1179 mem_align_alloc(mi_, size, (size) + 2, struct menu_item, 0xF)
1182 new_menu(enum menu_item_flags flags
)
1184 struct menu_item
*mi
= NULL
;
1186 if (realloc_menu_items(&mi
, 0)) mi
->flags
= flags
;
1192 add_to_menu(struct menu_item
**mi
, unsigned char *text
, unsigned char *rtext
,
1193 enum main_action action_id
, menu_func_T func
, void *data
,
1194 enum menu_item_flags flags
)
1196 int n
= count_items(*mi
);
1197 /* XXX: Don't clear the last and special item. */
1198 struct menu_item
*item
= realloc_menu_items(mi
, n
+ 1);
1204 /* Shift current last item by one place. */
1205 copy_struct(item
+ 1, item
);
1207 /* Setup the new item. All menu items share the item_free value. */
1208 SET_MENU_ITEM(item
, text
, rtext
, action_id
, func
, data
,
1209 item
->flags
| flags
, HKS_SHOW
, 0);
1212 #undef L_MAINMENU_SPACE
1213 #undef R_MAINMENU_SPACE
1214 #undef L_MAINTEXT_SPACE
1215 #undef R_MAINTEXT_SPACE
1216 #undef L_RTEXT_SPACE
1217 #undef R_RTEXT_SPACE
1220 #undef MENU_BORDER_SIZE