📄 listbox.c
字号:
/* Listbox widget implementation. *//* $Id: listbox.c,v 1.183.4.5 2005/04/06 08:32:41 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <string.h>#include "elinks.h"#include "bfu/dialog.h"#include "bfu/hierbox.h"#include "bfu/listbox.h"#include "config/kbdbind.h"#include "intl/gettext/libintl.h"#include "terminal/draw.h"#include "terminal/mouse.h"#include "terminal/terminal.h"#include "util/color.h"#include "util/conv.h"#include "util/lists.h"#define VERTICAL_LISTBOX_MARGIN 3voidadd_dlg_listbox(struct dialog *dlg, int height, void *box_data){ struct widget *widget; widget = &dlg->widgets[dlg->number_of_widgets++]; widget->type = WIDGET_LISTBOX; widget->info.listbox.height = height; widget->data = box_data;}struct listbox_data *get_listbox_widget_data(struct widget_data *widget_data){ assert(widget_data->widget->type == WIDGET_LISTBOX); return ((struct listbox_data *) widget_data->widget->data);}/* Layout for generic boxes */voiddlg_format_listbox(struct terminal *term, struct widget_data *widget_data, int x, int *y, int w, int max_height, int *rw, enum format_align align){ int min, optimal_h; set_box(&widget_data->box, x, *y, w, 1); if (rw) int_bounds(rw, widget_data->box.width, w); /* Height bussiness follows: */ /* We ignore this one happily now. Rather be Dynamic ;p. */ /* (*y) += widget_data->widget->gid; */ /* This is only weird heuristic, it could scale well I hope. */ optimal_h = max_height * 7 / 10 - VERTICAL_LISTBOX_MARGIN; min = get_opt_int("ui.dialogs.listbox_min_height"); if (max_height - VERTICAL_LISTBOX_MARGIN < min) { /* Big trouble: can't satisfy even the minimum :-(. */ widget_data->box.height = max_height - VERTICAL_LISTBOX_MARGIN; } else if (optimal_h < min) { widget_data->box.height = min; } else { widget_data->box.height = optimal_h; } /* DBG("::%d(%d)::%d::%d::", max_y, term?1:2, widget_data->h, *y); */ (*y) += widget_data->box.height;}/* *,item00->prev *|item00->root = NULL ,item10->prev *|item00->child <----->|item10->root *|item00->next <-. |item10->child [<->] *| | `item10->next *|item01->prev <-' *|item01->root = NULL *|item01->child [<->] *|item01->next <-. *| | *|item02->prev <-' *|item02->root = NULL ,item11->prev *|item02->child <----->|item11->root ,item20->prev *`item02->next \ |item11->child <----->|item20->root * | |item11->next <-. |item20->child [<->] * | | | `item20->next * | |item12->prev <-' * `-|item12->root * | |item12->child [<->] * | |item12->next <-. * | | | * | |item13->prev <-' * `-|item13->root ,item21->prev * |item13->child <----->|item21->root * `item13->next |item21->child [<->] * `item21->next * *//* Traverse a hierarchic tree from @item by @offset items, calling @fn, * if it is not NULL, on each item traversed (that is, each of the items * that we move _through_; this means from the passed @item up to, * but not including, the returned item). * * @offset may be negative to indicate that we should traverse upwards. * * Besides the current item, @fn is also passed @d, which is otherwise unused * by traverse_listbox_items_list, and a pointer to @offset, which @fn can set * to 0 to stop traversal or to other values to change the direction in which * or the number of items over which we will traverse. * * @fn should return 1 if it freed its passed item, or return 0 otherwise. * * If the passed @offset is zero, we set @offset to 1 and traverse thru * the list (down) until either we reach the end or @fn sets @offset to 0. *//* From the box structure, we should use only 'items' here. */struct listbox_item *traverse_listbox_items_list(struct listbox_item *item, struct listbox_data *box, int offset, int follow_visible, int (*fn)(struct listbox_item *, void *, int *), void *d){ struct listbox_item *visible_item = item; int levmove = 0; int stop = 0; int infinite = !offset; if (!item) return NULL; if (infinite) offset = 1; while (offset && !stop) { /* We need to cache these. Or what will happen if something * will free us item too early? However, we rely on item * being at least NULL in that case. */ /* There must be no orphaned listbox_items. No free()d roots * and no dangling children. */#define item_cache(item) \ do { \ croot = box->ops->get_root(item); cprev = item->prev; cnext = item->next; \ } while (0) struct listbox_item *croot, *cprev, *cnext; item_cache(item); if (fn && (!follow_visible || item->visible)) { if (fn(item, d, &offset)) { /* We was free()d! Let's try to carry on w/ the * cached coordinates. */ item = NULL; } if (!offset) { infinite = 0; /* safety (matches) */ continue; } } if (offset > 0) { /* Otherwise we climb back up when last item in root * is a folder. */ struct listbox_item *cragsman = NULL; /* Direction DOWN. */ if (!infinite) offset--; if (item && !list_empty(item->child) && item->expanded && (!follow_visible || item->visible)) { /* Descend to children. */ item = item->child.next; item_cache(item); goto done_down; } while (croot && (void *) cnext == &croot->child) { /* Last item in a non-root list, climb to your * root. */ if (!cragsman) cragsman = item; item = croot; item_cache(item); } if (!croot && (!cnext || (void *) cnext == box->items)) { /* Last item in the root list, quit.. */ stop = 1; if (cragsman) { /* ..and fall back where we were. */ item = cragsman; item_cache(item); } } /* We're not at the end of anything, go on. */ if (!stop) { item = cnext; item_cache(item); }done_down: if (!item || (follow_visible && !item->visible)) { offset++; } else { visible_item = item; } } else { /* Direction UP. */ if (!infinite) offset++; if (croot && (void *) cprev == &croot->child) { /* First item in a non-root list, climb to your * root. */ item = croot; item_cache(item); levmove = 1; } if (!croot && (void *) cprev == box->items) { /* First item in the root list, quit. */ stop = 1; levmove = 1; } /* We're not at the start of anything, go on. */ if (!levmove && !stop) { item = cprev; item_cache(item); while (item && !list_empty(item->child) && item->expanded && (!follow_visible || item->visible)) { /* Descend to children. */ item = item->child.prev; item_cache(item); } } else { levmove = 0; } if (!item || (follow_visible && !item->visible)) { offset--; } else { visible_item = item; } }#undef item_cache } return visible_item;}/* Takes care about listbox top moving. */static intlistbox_sel_move_do(struct listbox_item *item, void *data_, int *offset){ struct listbox_context *data = data_; if (item == data->box->top) data->box->sel_offset = 0; /* assure resync */ if (data->dist > 0) { if (data->box->sel_offset < data->widget_data->box.height - 1) { data->box->sel_offset++; } else { data->box->top = traverse_listbox_items_list(data->box->top, data->box, 1, 1, NULL, NULL); } } else if (data->dist < 0) { if (data->box->sel_offset > 0) { data->box->sel_offset--; } else { data->box->top = traverse_listbox_items_list(data->box->top, data->box, -1, 1, NULL, NULL); } } return 0;}/* Moves the selected item by [dist] items. If [dist] is out of the current * range, the selected item is moved to the extreme (ie, the top or bottom) */voidlistbox_sel_move(struct widget_data *widget_data, int dist){ struct listbox_data *box = get_listbox_widget_data(widget_data); if (!list_empty(*box->items)) { if (!box->top) box->top = box->items->next; if (!box->sel) box->sel = box->top; } /* We want to have these visible if possible. */ if (box->top && !box->top->visible) { box->top = traverse_listbox_items_list(box->top, box, 1, 1, NULL, NULL); box->sel = box->top; } if (traverse_listbox_items_list(box->sel, box, dist, 1, NULL, NULL) != box->sel) { struct listbox_context data; memset(&data, 0, sizeof(data)); data.box = box; data.widget_data = widget_data; data.dist = dist; /* XXX: This is ugly, yes; but we don't want to call the * callback if we won't move on at all. */ box->sel = traverse_listbox_items_list(box->sel, box, dist, 1, listbox_sel_move_do, &data); }}/* Takes care about rendering of each listbox item. */static intdisplay_listbox_item(struct listbox_item *item, void *data_, int *offset){ struct listbox_context *data = data_; unsigned char *stylename; int len; /* Length of the current text field. */ struct color_pair *color; int depth = item->depth + 1; int d; int x, y; stylename = (item == data->box->sel) ? "menu.selected" : ((item->marked) ? "menu.marked" : "menu.normal"); color = get_bfu_color(data->term, stylename); y = data->widget_data->box.y + data->offset; for (d = 0; d < depth - 1; d++) { struct listbox_item *root = item; struct listbox_item *child = item; int i, x;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -