📄 hierbox.c
字号:
/* Hiearchic listboxes browser dialog commons *//* $Id: hierbox.c,v 1.201.2.7 2005/05/01 21:05:57 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdarg.h>#include "elinks.h"#include "bfu/button.h"#include "bfu/dialog.h"#include "bfu/hierbox.h"#include "bfu/inpfield.h"#include "bfu/listbox.h"#include "bfu/msgbox.h"#include "bfu/text.h"#include "config/kbdbind.h"#include "dialogs/download.h"#include "intl/gettext/libintl.h"#include "protocol/uri.h"#include "sched/task.h"#include "terminal/screen.h"#include "terminal/tab.h"#include "terminal/terminal.h"struct hierbox_dialog_list_item { LIST_HEAD(struct hierbox_dialog_list_item); struct dialog_data *dlg_data;};voidupdate_hierbox_browser(struct hierbox_browser *browser){ struct hierbox_dialog_list_item *item; foreach (item, browser->dialogs) { redraw_from_window(item->dlg_data->win->next); }}/* Common backend for listbox adding */struct listbox_item *add_listbox_item(struct hierbox_browser *browser, struct listbox_item *root, enum listbox_item_type type, void *data, int add_position){ struct listbox_item *item; if (!root) { assertm(browser, "Nowhere to add new list box item"); root = &browser->root; } item = mem_calloc(1, sizeof(*item)); if (!item) return NULL; init_list(item->child); item->visible = 1; item->udata = data; item->type = type; item->depth = root->depth + 1; /* TODO: Possibility to sort by making add_position into a flag */ if (add_position < 0) add_to_list_end(root->child, item); else add_to_list(root->child, item); if (browser) update_hierbox_browser(browser); return item;}/* Find a listbox item to replace @item. This is done by trying first to * traverse down then up, and if both traversals end up returning the @item * (that is, it is the last item in the box), return NULL. */static inline struct listbox_item *replace_listbox_item(struct listbox_item *item, struct listbox_data *data){ struct listbox_item *box; box = traverse_listbox_items_list(item, data, 1, 1, NULL, NULL); if (item != box) return box; box = traverse_listbox_items_list(item, data, -1, 1, NULL, NULL); return (item == box) ? NULL : box;}voiddone_listbox_item(struct hierbox_browser *browser, struct listbox_item *box_item){ struct listbox_data *box_data; assert(box_item && list_empty(box_item->child)); /* If we are removing the top or the selected box we have to figure out * a replacement. */ foreach (box_data, browser->boxes) { if (box_data->sel == box_item) box_data->sel = replace_listbox_item(box_item, box_data); if (box_data->top == box_item) box_data->top = replace_listbox_item(box_item, box_data); } /* The option dialog needs this test */ if (box_item->next) del_from_list(box_item); mem_free(box_item); update_hierbox_browser(browser);}static voidrecursively_set_expanded(struct listbox_item *box, int expanded){ struct listbox_item *child; if (box->type != BI_FOLDER) return; box->expanded = expanded; foreach (child, box->child) recursively_set_expanded(child, expanded);}static inttest_search(struct listbox_item *item, void *data_, int *offset){ struct listbox_context *listbox_context = data_; listbox_context->offset--; if (item == listbox_context->box->sel) *offset = 0; return 0;}static t_handler_event_statushierbox_ev_kbd(struct dialog_data *dlg_data){ struct hierbox_browser *browser = dlg_data->dlg->udata2; struct widget_data *widget_data = dlg_data->widgets_data; struct widget *widget = widget_data->widget; struct listbox_data *box; struct listbox_item *selected; enum menu_action action; struct term_event *ev = dlg_data->term_event; /* Check if listbox has something to say to this */ if (widget->ops->kbd && widget->ops->kbd(dlg_data, widget_data) == EVENT_PROCESSED) return EVENT_PROCESSED; box = get_dlg_listbox_data(dlg_data); selected = box->sel; action = kbd_action(KEYMAP_MENU, ev, NULL); if (action == ACT_MENU_SELECT) { if (!selected) return EVENT_PROCESSED; if (selected->type != BI_FOLDER) return EVENT_NOT_PROCESSED; selected->expanded = !selected->expanded; } else if (action == ACT_MENU_UNEXPAND) { /* Recursively unexpand all folders */ if (!selected) return EVENT_PROCESSED; /* Special trick: if the folder is already * folded, jump at the parent folder, so the * next time when user presses the key, the * whole parent folder will be closed. */ if (list_empty(selected->child) || !selected->expanded) { struct listbox_item *root = box->ops->get_root(selected); if (root) { struct listbox_context ctx; memset(&ctx, 0, sizeof(ctx)); ctx.box = box; ctx.offset = 1; traverse_listbox_items_list( root, box, 0, 1, test_search, &ctx); listbox_sel_move(dlg_data->widgets_data, ctx.offset); } } else if (selected->type == BI_FOLDER) { recursively_set_expanded(selected, 0); } } else if (action == ACT_MENU_EXPAND) { /* Recursively expand all folders */ if (!selected || box->sel->type != BI_FOLDER) return EVENT_PROCESSED; recursively_set_expanded(box->sel, 1); } else if (action == ACT_MENU_SEARCH) { if (!box->ops->match) return EVENT_NOT_PROCESSED; push_hierbox_search_button(dlg_data, NULL); return EVENT_PROCESSED; } else { return EVENT_NOT_PROCESSED; } if (browser->expansion_callback) browser->expansion_callback(); display_widget(dlg_data, dlg_data->widgets_data); return EVENT_PROCESSED;}static t_handler_event_statushierbox_ev_init(struct dialog_data *dlg_data){ struct hierbox_browser *browser = dlg_data->dlg->udata2; struct hierbox_dialog_list_item *item; struct listbox_item *litem; /* If we fail here it only means automatic updating * will not be possible so no need to panic. */ item = mem_alloc(sizeof(*item)); if (item) { item->dlg_data = dlg_data; add_to_list(browser->dialogs, item); } foreach (litem, browser->root.child) { litem->visible = 1; } return EVENT_NOT_PROCESSED; /* FIXME: is this correct ? --Zas */}static t_handler_event_statushierbox_ev_abort(struct dialog_data *dlg_data){ struct listbox_data *box = get_dlg_listbox_data(dlg_data); struct hierbox_browser *browser = dlg_data->dlg->udata2; struct hierbox_dialog_list_item *item; /* Save state and delete the box structure */ if (!browser->do_not_save_state) copy_struct(&browser->box_data, box); del_from_list(box); /* Delete the dialog list entry */ foreach (item, browser->dialogs) { if (item->dlg_data == dlg_data) { del_from_list(item); mem_free(item); break; } } return EVENT_NOT_PROCESSED; /* FIXME: is this correct ? --Zas */}/* We install own dialog event handler, so that we can give the listbox widget * an early chance to catch the event. Basically, the listbox widget is itself * unselectable, instead one of the buttons below is always active. So, we * always first let the listbox catch the keypress and handle it, and if it * doesn't care, we pass it on to the button. */static t_handler_event_statushierbox_dialog_event_handler(struct dialog_data *dlg_data){ struct term_event *ev = dlg_data->term_event; switch (ev->ev) { case EVENT_KBD: return hierbox_ev_kbd(dlg_data); case EVENT_INIT: return hierbox_ev_init(dlg_data); case EVENT_RESIZE: case EVENT_REDRAW: case EVENT_MOUSE: return EVENT_NOT_PROCESSED; case EVENT_ABORT: return hierbox_ev_abort(dlg_data); } return EVENT_NOT_PROCESSED;}struct dialog_data *hierbox_browser(struct hierbox_browser *browser, struct session *ses){ struct terminal *term = ses->tab->term; struct listbox_data *listbox_data; struct dialog *dlg; int button = browser->buttons_size + 2; int anonymous = get_cmd_opt_bool("anonymous"); assert(ses); dlg = calloc_dialog(button, sizeof(*listbox_data)); if (!dlg) return NULL; listbox_data = (struct listbox_data *) get_dialog_offset(dlg, button); dlg->title = _(browser->title, term); dlg->layouter = generic_dialog_layouter; dlg->layout.maximize_width = 1; dlg->layout.padding_top = 1; dlg->handle_event = hierbox_dialog_event_handler; dlg->udata = ses; dlg->udata2 = browser; add_dlg_listbox(dlg, 12, listbox_data); for (button = 0; button < browser->buttons_size; button++) { struct hierbox_browser_button *but = &browser->buttons[button]; /* Skip buttons that should not be displayed in anonymous mode */ if (anonymous && !but->anonymous) { anonymous++; continue; } add_dlg_button(dlg, _(but->label, term), B_ENTER, but->handler, NULL); } add_dlg_button(dlg, _("Close", term), B_ESC, cancel_dialog, NULL); /* @anonymous was initially 1 if we are running in anonymous mode so we * have to subtract one. */ add_dlg_end(dlg, button + 2 - (anonymous ? anonymous - 1 : 0)); return do_dialog(term, dlg, getml(dlg, NULL));}/* Action info management */static intscan_for_marks(struct listbox_item *item, void *info_, int *offset){ if (item->marked) { struct listbox_context *context = info_; context->item = NULL; *offset = 0; } return 0;}static intscan_for_used(struct listbox_item *item, void *info_, int *offset){ struct listbox_context *context = info_; if (context->box->ops->is_used(item)) { context->item = item; *offset = 0; } return 0;}static struct listbox_context *init_listbox_context(struct listbox_data *box, struct terminal *term, struct listbox_item *item, int (*scanner)(struct listbox_item *, void *, int *)){ struct listbox_context *context; context = mem_calloc(1, sizeof(*context)); if (!context) return NULL; context->item = item; context->term = term; context->box = box; if (!scanner) return context; /* Look if it wouldn't be more interesting to blast off the marked * item. */ assert(!list_empty(*box->items)); traverse_listbox_items_list(box->items->next, box, 0, 0, scanner, context); return context;}static voiddone_listbox_context(void *context_){ struct listbox_context *context = context_; if (context->item) context->box->ops->unlock(context->item);}/* Info action */t_handler_event_statuspush_hierbox_info_button(struct dialog_data *dlg_data, struct widget_data *button){ struct listbox_data *box = get_dlg_listbox_data(dlg_data); struct terminal *term = dlg_data->win->term; struct listbox_context *context; unsigned char *msg; if (!box->sel) return EVENT_PROCESSED; assert(box->ops); context = init_listbox_context(box, term, box->sel, NULL); if (!context) return EVENT_PROCESSED; msg = box->ops->get_info(context->item, term); if (!msg) { mem_free(context); if (box->sel->type == BI_FOLDER) { info_box(term, 0, N_("Info"), ALIGN_CENTER, N_("Press space to expand this folder.")); } return EVENT_PROCESSED; } box->ops->lock(context->item); msg_box(term, getml(context, NULL), MSGBOX_FREE_TEXT /* | MSGBOX_SCROLLABLE */, N_("Info"), ALIGN_LEFT, msg, context, 1, N_("~OK"), done_listbox_context, B_ESC | B_ENTER); return EVENT_PROCESSED;}/* Goto action */static voidrecursively_goto_listbox(struct session *ses, struct listbox_item *root, struct listbox_data *box){ struct listbox_item *item; foreach (item, root->child) { struct uri *uri; if (item->type == BI_FOLDER) { recursively_goto_listbox(ses, item, box); continue; } uri = box->ops->get_uri(item); if (!uri) continue; open_uri_in_new_tab(ses, uri, 1, 0); done_uri(uri); }}static intgoto_marked(struct listbox_item *item, void *data_, int *offset){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -