?? menu.c
字號:
/* Menu system implementation. *//* $Id: menu.c,v 1.286.4.2 2005/04/05 21:08:40 jonas Exp $ */#ifndef _GNU_SOURCE#define _GNU_SOURCE /* XXX: we _WANT_ strcasestr() ! */#endif#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdlib.h>#include <string.h>#include "elinks.h"#include "bfu/hotkey.h"#include "bfu/inpfield.h"#include "bfu/menu.h"#include "config/kbdbind.h"#include "intl/gettext/libintl.h"#include "sched/action.h"#include "terminal/draw.h"#include "terminal/event.h"#include "terminal/kbd.h"#include "terminal/mouse.h"#include "terminal/tab.h"#include "terminal/terminal.h"#include "terminal/window.h"#include "util/color.h"#include "util/conv.h"#include "util/memory.h"/* Left and right main menu reserved spaces. */#define L_MAINMENU_SPACE 2#define R_MAINMENU_SPACE 2/* Left and right padding spaces around labels in main menu. */#define L_MAINTEXT_SPACE 1#define R_MAINTEXT_SPACE 1/* Spaces before and after right text of submenu. */#define L_RTEXT_SPACE 1#define R_RTEXT_SPACE 1/* Spaces before and after left text of submenu. */#define L_TEXT_SPACE 1#define R_TEXT_SPACE 1/* Border size in submenu. */#define MENU_BORDER_SIZE 1/* Types and structures *//* Submenu indicator, displayed at right. */static unsigned char m_submenu[] = ">>";static int m_submenu_len = sizeof(m_submenu) - 1;/* Prototypes */static window_handler menu_handler;static window_handler mainmenu_handler;static inline intcount_items(struct menu_item *items){ struct menu_item *item; int i = 0; if (items) { foreach_menu_item (item, items) { i++; } } return i;}static voidfree_menu_items(struct menu_item *items){ struct menu_item *item; if (!items) return; /* Note that flags & FREE_DATA applies only when menu is aborted; * it is zeroed when some menu field is selected. */ foreach_menu_item (item, items) { if (item->flags & FREE_TEXT) mem_free_if(item->text); if (item->flags & FREE_RTEXT) mem_free_if(item->rtext); if (item->flags & FREE_DATA) mem_free_if(item->data); } mem_free(items);}voiddo_menu_selected(struct terminal *term, struct menu_item *items, void *data, int selected, int hotkeys){ struct menu *menu = mem_calloc(1, sizeof(*menu)); if (menu) { menu->selected = selected; menu->items = items; menu->data = data; menu->size = count_items(items); menu->hotkeys = hotkeys;#ifdef ENABLE_NLS menu->lang = -1;#endif refresh_hotkeys(term, menu); add_window(term, menu_handler, menu); } else if (items->flags & FREE_ANY) { free_menu_items(items); }}voiddo_menu(struct terminal *term, struct menu_item *items, void *data, int hotkeys){ do_menu_selected(term, items, data, 0, hotkeys);}static voidselect_menu_item(struct terminal *term, struct menu_item *it, void *data){ /* We save these values due to delete_window() call below. */ menu_func func = it->func; void *it_data = it->data; enum main_action action = it->action; if (!mi_is_selectable(it)) return; if (!mi_is_submenu(it)) { /* Don't free data! */ it->flags &= ~FREE_DATA; while (!list_empty(term->windows)) { struct window *win = term->windows.next; if (win->handler != menu_handler && win->handler != mainmenu_handler) break; delete_window(win); } } if (action != ACT_MAIN_NONE && !func) { struct session *ses = data; do_action(ses, action, 1); return; } assertm(func, "No menu function"); if_assert_failed return; func(term, it_data, data);}static inline voidselect_menu(struct terminal *term, struct menu *menu){ if (menu->selected < 0 || menu->selected >= menu->size) return; select_menu_item(term, &menu->items[menu->selected], menu->data);}/* Get desired width for left text in menu item, accounting spacing. */static intget_menuitem_text_width(struct terminal *term, struct menu_item *mi){ unsigned char *text; if (!mi_has_left_text(mi)) return 0; text = mi->text; if (mi_text_translate(mi)) text = _(text, term); if (!text[0]) return 0; return L_TEXT_SPACE + strlen(text) - !!mi->hotkey_pos + R_TEXT_SPACE;}/* Get desired width for right text in menu item, accounting spacing. */static intget_menuitem_rtext_width(struct terminal *term, struct menu_item *mi){ int rtext_width = 0; if (mi_is_submenu(mi)) { rtext_width = L_RTEXT_SPACE + m_submenu_len + R_RTEXT_SPACE; } else if (mi->action != ACT_MAIN_NONE) { struct string keystroke; if (init_string(&keystroke)) { add_keystroke_to_string(&keystroke, mi->action, KEYMAP_MAIN); rtext_width = L_RTEXT_SPACE + keystroke.length + R_RTEXT_SPACE; done_string(&keystroke); } } else if (mi_has_right_text(mi)) { unsigned char *rtext = mi->rtext; if (mi_rtext_translate(mi)) rtext = _(rtext, term); if (rtext[0]) rtext_width = L_RTEXT_SPACE + strlen(rtext) + R_RTEXT_SPACE; } return rtext_width;}static intget_menuitem_width(struct terminal *term, struct menu_item *mi, int max_width){ int text_width = get_menuitem_text_width(term, mi); int rtext_width = get_menuitem_rtext_width(term, mi); int_upper_bound(&text_width, max_width); int_upper_bound(&rtext_width, max_width - text_width); return text_width + rtext_width;}static voidcount_menu_size(struct terminal *term, struct menu *menu){ struct menu_item *item; int width = term->width - MENU_BORDER_SIZE * 2; int height = term->height - MENU_BORDER_SIZE * 2; int my = int_min(menu->size, height); int mx = 0; foreach_menu_item (item, menu->items) { int_lower_bound(&mx, get_menuitem_width(term, item, width)); } set_box(&menu->box, menu->parent_x, menu->parent_y, mx + MENU_BORDER_SIZE * 2, my + MENU_BORDER_SIZE * 2); int_bounds(&menu->box.x, 0, width - mx); int_bounds(&menu->box.y, 0, height - my);}static intsearch_selectable(struct menu *menu, int pos, int dir){ assert(pos >= 0 && pos < menu->size && (dir == 1 || dir == -1)); if_assert_failed return -1; while (!mi_is_selectable(&menu->items[pos])) { if (dir > 0 && pos == menu->size - 1) return -1; else if (dir < 0 && pos == 0) return -1; pos += dir; } return pos;}static voidscroll_menu(struct menu *menu, int steps, int wrap){ int height, scr_i, pos; int s = steps ? steps/abs(steps) : 1; /* Selectable item search direction. */ if (menu->size <= 0) { /* Menu is empty. */ menu->selected = -1; menu->first = 0; return; } /* Move by required steps and handle wraparound if needed. * A step of zero can be used, indicating we want to select * item corresponding to |menu->selected| value rather than * moving by to a position relative to this value. * We override search direction for selectable items if we encounter * a limit, since it depends in which conditions this limit is * attained. */ menu->selected += steps; if (menu->selected >= menu->size) { if (wrap) { menu->selected = 0; s = 1; } else { menu->selected = int_max(0, menu->size - 1); s = -1; } } else if (menu->selected < 0) { if (wrap) { menu->selected = int_max(0, menu->size - 1); s = -1; } else { menu->selected = 0; s = 1; } } /* Current selected item may be an unselectable item, so we need to * find first selectable item near to it. * @s = 1 : ascending search. * @s = -1: descending search. */ /* Search first selectable item in one direction. */ pos = search_selectable(menu, menu->selected, s); if (pos == -1) { /* If not found, invert the search direction and try again. */ pos = search_selectable(menu, menu->selected, -s); } /* No selectable item found, just return. */ if (pos == -1) { menu->selected = -1; menu->first = 0; } menu->selected = pos; height = int_max(1, menu->box.height - MENU_BORDER_SIZE * 2); /* The rest is not needed for horizontal menus like the mainmenu. * FIXME: We need a better way to figure out which menus are horizontal and * which are vertical (normal) --jonas */ if (height == 1) return; scr_i = int_min((height - 1) / 2, SCROLL_ITEMS); int_bounds(&menu->first, menu->selected - height + scr_i + 1, menu->selected - scr_i); int_bounds(&menu->first, 0, menu->size - height);}static inline voiddraw_menu_left_text(struct terminal *term, unsigned char *text, int len, int x, int y, int width, struct color_pair *color){ int w = width - (L_TEXT_SPACE + R_TEXT_SPACE); if (w <= 0) return; if (len < 0) len = strlen(text); if (!len) return; if (len > w) len = w; draw_text(term, x + L_TEXT_SPACE, y, text, len, 0, color);}static inline voiddraw_menu_left_text_hk(struct terminal *term, unsigned char *text, int hotkey_pos, int x, int y, int width, struct color_pair *color, int selected){ struct color_pair *hk_color = get_bfu_color(term, "menu.hotkey.normal"); struct color_pair *hk_color_sel = get_bfu_color(term, "menu.hotkey.selected"); enum screen_char_attr hk_attr = get_opt_bool("ui.dialogs.underline_hotkeys") ? SCREEN_ATTR_UNDERLINE : 0; unsigned char c; int xbase = x + L_TEXT_SPACE; int w = width - (L_TEXT_SPACE + R_TEXT_SPACE); int hk_state = 0;#ifdef CONFIG_DEBUG /* For redundant hotkeys highlighting. */ int double_hk = 0; if (hotkey_pos < 0) hotkey_pos = -hotkey_pos, double_hk = 1;#endif if (!hotkey_pos || w <= 0) return; if (selected) { struct color_pair *tmp = hk_color; hk_color = hk_color_sel; hk_color_sel = tmp; } for (x = 0; x - !!hk_state < w && (c = text[x]); x++) { if (!hk_state && x == hotkey_pos - 1) { hk_state = 1; continue; } if (hk_state == 1) {#ifdef CONFIG_DEBUG draw_char(term, xbase + x - 1, y, c, hk_attr, (double_hk ? hk_color_sel : hk_color));#else draw_char(term, xbase + x - 1, y, c, hk_attr, hk_color);#endif hk_state = 2; } else { draw_char(term, xbase + x - !!hk_state, y, c, 0, color); } }}static inline voiddraw_menu_right_text(struct terminal *term, unsigned char *text, int len, int x, int y, int width, struct color_pair *color){ int w = width - (L_RTEXT_SPACE + R_RTEXT_SPACE); if (w <= 0) return; if (len < 0) len = strlen(text); if (!len) return; if (len > w) len = w; x += w - len + L_RTEXT_SPACE + L_TEXT_SPACE; draw_text(term, x, y, text, len, 0, color);}static voiddisplay_menu(struct terminal *term, struct menu *menu){ struct color_pair *normal_color = get_bfu_color(term, "menu.normal"); struct color_pair *selected_color = get_bfu_color(term, "menu.selected"); struct color_pair *frame_color = get_bfu_color(term, "menu.frame"); struct box box; int p; int menu_height; set_box(&box, menu->box.x + MENU_BORDER_SIZE, menu->box.y + MENU_BORDER_SIZE, int_max(0, menu->box.width - MENU_BORDER_SIZE * 2), int_max(0, menu->box.height - MENU_BORDER_SIZE * 2)); draw_box(term, &box, ' ', 0, normal_color); draw_border(term, &box, frame_color, 1); menu_height = box.height; box.height = 1; for (p = menu->first; p < menu->size && p < menu->first + menu_height; p++, box.y++) { struct color_pair *color = normal_color; struct menu_item *mi = &menu->items[p]; int selected = (p == menu->selected);#ifdef CONFIG_DEBUG /* Sanity check. */ if (mi_is_end_of_menu(mi)) INTERNAL("Unexpected end of menu [%p:%d]", mi, p);#endif if (selected) { /* This entry is selected. */ color = selected_color; set_cursor(term, box.x, box.y, 1); set_window_ptr(menu->win, menu->box.x + menu->box.width, box.y); draw_box(term, &box, ' ', 0, color); } if (mi_is_horizontal_bar(mi)) { /* Horizontal separator */ draw_border_char(term, menu->box.x, box.y, BORDER_SRTEE, frame_color); draw_box(term, &box, BORDER_SHLINE, SCREEN_ATTR_FRAME, frame_color); draw_border_char(term, box.x + box.width, box.y, BORDER_SLTEE, frame_color); continue; } if (mi_has_left_text(mi)) { int l = mi->hotkey_pos; unsigned char *text = mi->text; if (mi_text_translate(mi)) text = _(text, term); if (!mi_is_selectable(mi)) l = 0; if (l) { draw_menu_left_text_hk(term, text, l, box.x, box.y, box.width, color, selected); } else { draw_menu_left_text(term, text, -1, box.x, box.y, box.width, color); } } if (mi_is_submenu(mi)) { draw_menu_right_text(term, m_submenu, m_submenu_len, menu->box.x, box.y, box.width, color); } else if (mi->action != ACT_MAIN_NONE) { struct string keystroke;#ifdef CONFIG_DEBUG /* Help to detect action + right text. --Zas */ if (mi_has_right_text(mi)) { if (color == selected_color) color = normal_color; else color = selected_color; }#endif /* CONFIG_DEBUG */ if (init_string(&keystroke)) { add_keystroke_to_string(&keystroke, mi->action, KEYMAP_MAIN); draw_menu_right_text(term, keystroke.source, keystroke.length, menu->box.x, box.y, box.width, color); done_string(&keystroke); } } else if (mi_has_right_text(mi)) { unsigned char *rtext = mi->rtext; if (mi_rtext_translate(mi)) rtext = _(rtext, term); if (*rtext) { /* There's a right text, so print it */ draw_menu_right_text(term, rtext, -1, menu->box.x, box.y, box.width, color); } } } redraw_from_window(menu->win);}#ifdef CONFIG_MOUSEstatic voidmenu_mouse_handler(struct menu *menu, struct term_event *ev){ struct window *win = menu->win; int scroll_direction = 1; switch (get_mouse_button(ev)) { case B_WHEEL_UP: scroll_direction = -1; /* Fall thru */ case B_WHEEL_DOWN: if (check_mouse_action(ev, B_DOWN)) { scroll_menu(menu, scroll_direction, 1); display_menu(win->term, menu); } return; } if (!check_mouse_position(ev, &menu->box)) { if (check_mouse_action(ev, B_DOWN)) { delete_window_ev(win, ev); } else { struct window *w1; struct window *end = (struct window *) &win->term->windows; for (w1 = win; w1 != end; w1 = w1->next) { struct menu *m1; if (w1->handler == mainmenu_handler) { if (!ev->info.mouse.y) delete_window_ev(win, ev); break; } if (w1->handler != menu_handler) break; m1 = w1->data; if (check_mouse_position(ev, &m1->box)) { delete_window_ev(win, ev); break; } } } } else { int sel = ev->info.mouse.y - menu->box.y - 1 + menu->first; if (sel >= 0 && sel < menu->size && mi_is_selectable(&menu->items[sel])) { menu->selected = sel;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -