bug 153, 1066: Convert properties of SMJS bookmark to/from UTF-8.
[elinks.git] / src / bfu / hierbox.c
blob38fe5ed4ab2ceaf13623c78f62a9cf6afedf1384
1 /* Hiearchic listboxes browser dialog commons */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdarg.h>
9 #include "elinks.h"
11 #include "bfu/button.h"
12 #include "bfu/dialog.h"
13 #include "bfu/hierbox.h"
14 #include "bfu/inpfield.h"
15 #include "bfu/listbox.h"
16 #include "bfu/msgbox.h"
17 #include "bfu/text.h"
18 #include "config/kbdbind.h"
19 #include "intl/gettext/libintl.h"
20 #include "protocol/uri.h"
21 #include "session/task.h"
22 #include "terminal/screen.h"
23 #include "terminal/tab.h"
24 #include "terminal/terminal.h"
27 void
28 update_hierbox_browser(struct hierbox_browser *browser)
30 struct hierbox_dialog_list_item *item;
32 foreach (item, browser->dialogs) {
33 redraw_from_window(item->dlg_data->win->next);
38 /* Common backend for listbox adding */
39 struct listbox_item *
40 add_listbox_item(struct hierbox_browser *browser, struct listbox_item *root,
41 enum listbox_item_type type, void *data, int add_position)
43 struct listbox_item *item;
45 if (!root) {
46 assertm(browser != NULL, "Nowhere to add new list box item");
47 root = &browser->root;
50 item = mem_calloc(1, sizeof(*item));
51 if (!item) return NULL;
53 init_list(item->child);
54 item->visible = 1;
56 item->udata = data;
57 item->type = type;
58 item->depth = root->depth + 1;
60 /* TODO: Possibility to sort by making add_position into a flag */
61 if (add_position < 0)
62 add_to_list_end(root->child, item);
63 else
64 add_to_list(root->child, item);
66 if (browser) update_hierbox_browser(browser);
68 return item;
72 /* Find a listbox item to replace @item. This is done by trying first to
73 * traverse down then up, and if both traversals end up returning the @item
74 * (that is, it is the last item in the box), return NULL. */
75 static inline struct listbox_item *
76 replace_listbox_item(struct listbox_item *item, struct listbox_data *data)
78 struct listbox_item *new_item;
80 new_item = traverse_listbox_items_list(item, data, 1, 1, NULL, NULL);
81 if (item != new_item) return new_item;
83 new_item = traverse_listbox_items_list(item, data, -1, 1, NULL, NULL);
84 return (item == new_item) ? NULL : new_item;
87 void
88 done_listbox_item(struct hierbox_browser *browser, struct listbox_item *item)
90 struct listbox_data *box_data;
92 assert(item && list_empty(item->child));
93 if_assert_failed return;
95 /* The option dialog needs this test */
96 if (item->next) {
97 /* If we are removing the top or the selected box
98 * we have to figure out a replacement. */
100 foreach (box_data, browser->boxes) {
101 if (box_data->sel == item)
102 box_data->sel = replace_listbox_item(item,
103 box_data);
105 if (box_data->top == item)
106 box_data->top = replace_listbox_item(item,
107 box_data);
110 del_from_list(item);
112 update_hierbox_browser(browser);
115 mem_free(item);
119 static void
120 recursively_set_expanded(struct listbox_item *item, int expanded)
122 struct listbox_item *child;
124 if (item->type != BI_FOLDER)
125 return;
127 item->expanded = expanded;
129 foreach (child, item->child)
130 recursively_set_expanded(child, expanded);
133 static widget_handler_status_T
134 hierbox_ev_kbd(struct dialog_data *dlg_data)
136 struct hierbox_browser *browser = dlg_data->dlg->udata2;
137 struct widget_data *widget_data = dlg_data->widgets_data;
138 struct widget *widget = widget_data->widget;
139 struct listbox_data *box;
140 struct listbox_item *selected;
141 enum menu_action action_id;
142 struct term_event *ev = dlg_data->term_event;
144 /* Check if listbox has something to say to this */
145 if (widget->ops->kbd
146 && widget->ops->kbd(dlg_data, widget_data)
147 == EVENT_PROCESSED)
148 return EVENT_PROCESSED;
150 box = get_dlg_listbox_data(dlg_data);
151 selected = box->sel;
152 action_id = kbd_action(KEYMAP_MENU, ev, NULL);
154 switch (action_id) {
155 case ACT_MENU_SELECT:
156 if (!selected) return EVENT_PROCESSED;
157 if (selected->type != BI_FOLDER)
158 return EVENT_NOT_PROCESSED;
159 selected->expanded = !selected->expanded;
160 break;
162 case ACT_MENU_UNEXPAND:
163 /* Recursively unexpand all folders */
164 if (!selected) return EVENT_PROCESSED;
166 /* Special trick: if the folder is already
167 * folded, jump to the parent folder, so the
168 * next time when user presses the key, the
169 * whole parent folder will be closed. */
170 if (list_empty(selected->child)
171 || !selected->expanded) {
172 struct listbox_item *root;
174 root = box->ops->get_root(selected);
175 if (root) {
176 listbox_sel(widget_data, root);
179 } else if (selected->type == BI_FOLDER) {
180 recursively_set_expanded(selected, 0);
182 break;
184 case ACT_MENU_EXPAND:
185 /* Recursively expand all folders */
187 if (!selected || selected->type != BI_FOLDER)
188 return EVENT_PROCESSED;
190 recursively_set_expanded(selected, 1);
191 break;
193 case ACT_MENU_SEARCH:
194 if (!box->ops->match)
195 return EVENT_NOT_PROCESSED;
197 push_hierbox_search_button(dlg_data, NULL);
198 return EVENT_PROCESSED;
200 default:
201 return EVENT_NOT_PROCESSED;
205 if (browser->expansion_callback)
206 browser->expansion_callback();
208 display_widget(dlg_data, widget_data);
210 return EVENT_PROCESSED;
213 static widget_handler_status_T
214 hierbox_ev_init(struct dialog_data *dlg_data)
216 struct hierbox_browser *browser = dlg_data->dlg->udata2;
217 struct hierbox_dialog_list_item *item;
218 struct listbox_item *litem;
220 /* If we fail here it only means automatic updating
221 * will not be possible so no need to panic. */
222 item = mem_alloc(sizeof(*item));
223 if (item) {
224 item->dlg_data = dlg_data;
225 add_to_list(browser->dialogs, item);
228 foreach (litem, browser->root.child) {
229 litem->visible = 1;
232 /* Return this so that the generic dialog code will run and initialise
233 * the widgets and stuff. */
234 return EVENT_NOT_PROCESSED;
237 static widget_handler_status_T
238 hierbox_ev_abort(struct dialog_data *dlg_data)
240 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
241 struct hierbox_browser *browser = dlg_data->dlg->udata2;
242 struct hierbox_dialog_list_item *item;
244 /* Save state and delete the box structure */
245 if (!browser->do_not_save_state)
246 copy_struct(&browser->box_data, box);
247 del_from_list(box);
249 /* Delete the dialog list entry */
250 foreach (item, browser->dialogs) {
251 if (item->dlg_data == dlg_data) {
252 del_from_list(item);
253 mem_free(item);
254 break;
258 /* Return this so that the generic dialog code will run and initialise
259 * the widgets and stuff. */
260 return EVENT_NOT_PROCESSED;
264 /* We install own dialog event handler, so that we can give the listbox widget
265 * an early chance to catch the event. Basically, the listbox widget is itself
266 * unselectable, instead one of the buttons below is always active. So, we
267 * always first let the listbox catch the keypress and handle it, and if it
268 * doesn't care, we pass it on to the button. */
269 static widget_handler_status_T
270 hierbox_dialog_event_handler(struct dialog_data *dlg_data)
272 struct term_event *ev = dlg_data->term_event;
274 switch (ev->ev) {
275 case EVENT_KBD:
276 return hierbox_ev_kbd(dlg_data);
278 case EVENT_INIT:
279 return hierbox_ev_init(dlg_data);
281 case EVENT_RESIZE:
282 case EVENT_REDRAW:
283 case EVENT_MOUSE:
284 return EVENT_NOT_PROCESSED;
286 case EVENT_ABORT:
287 return hierbox_ev_abort(dlg_data);
290 return EVENT_NOT_PROCESSED;
294 struct dialog_data *
295 hierbox_browser(struct hierbox_browser *browser, struct session *ses)
297 struct terminal *term = ses->tab->term;
298 struct listbox_data *listbox_data;
299 struct dialog *dlg;
300 int button = browser->buttons_size + 2;
301 int anonymous = get_cmd_opt_bool("anonymous");
303 assert(ses);
305 dlg = calloc_dialog(button, sizeof(*listbox_data));
306 if (!dlg) return NULL;
308 listbox_data = (struct listbox_data *) get_dialog_offset(dlg, button);
310 dlg->title = _(browser->title, term);
311 dlg->layouter = generic_dialog_layouter;
312 dlg->layout.maximize_width = 1;
313 dlg->layout.padding_top = 1;
314 dlg->handle_event = hierbox_dialog_event_handler;
315 dlg->udata = ses;
316 dlg->udata2 = browser;
318 add_dlg_listbox(dlg, listbox_data);
320 for (button = 0; button < browser->buttons_size; button++) {
321 const struct hierbox_browser_button *but = &browser->buttons[button];
323 /* Skip buttons that should not be displayed in anonymous mode */
324 if (anonymous && !but->anonymous) {
325 anonymous++;
326 continue;
329 add_dlg_button(dlg, _(but->label, term), B_ENTER, but->handler, NULL);
332 add_dlg_button(dlg, _("Close", term), B_ESC, cancel_dialog, NULL);
334 /* @anonymous was initially 1 if we are running in anonymous mode so we
335 * have to subtract one. */
336 add_dlg_end(dlg, button + 2 - (anonymous ? anonymous - 1 : 0));
338 return do_dialog(term, dlg, getml(dlg, (void *) NULL));
342 /* Action info management */
344 static int
345 scan_for_marks(struct listbox_item *item, void *info_, int *offset)
347 if (item->marked) {
348 struct listbox_context *context = info_;
350 context->item = NULL;
351 *offset = 0;
354 return 0;
357 static int
358 scan_for_used(struct listbox_item *item, void *info_, int *offset)
360 struct listbox_context *context = info_;
362 if (context->box->ops->is_used(item)) {
363 context->item = item;
364 *offset = 0;
367 return 0;
371 static struct listbox_context *
372 init_listbox_context(struct listbox_data *box, struct terminal *term,
373 struct listbox_item *item,
374 int (*scanner)(struct listbox_item *, void *, int *))
376 struct listbox_context *context;
378 context = mem_calloc(1, sizeof(*context));
379 if (!context) return NULL;
381 context->item = item;
382 context->term = term;
383 context->box = box;
385 if (!scanner) return context;
387 /* Look if it wouldn't be more interesting to blast off the marked
388 * item. */
389 assert(!list_empty(*box->items));
390 traverse_listbox_items_list(box->items->next, box, 0, 0,
391 scanner, context);
393 return context;
396 static void
397 done_listbox_context(void *context_)
399 struct listbox_context *context = context_;
401 if (context->item)
402 context->box->ops->unlock(context->item);
406 /* Info action */
408 widget_handler_status_T
409 push_hierbox_info_button(struct dialog_data *dlg_data, struct widget_data *button)
411 /* [gettext_accelerator_context(push_hierbox_info_button)] */
412 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
413 struct listbox_item *item = box->sel;
414 struct terminal *term = dlg_data->win->term;
415 struct listbox_context *context;
416 unsigned char *msg;
418 if (!item) return EVENT_PROCESSED;
420 assert(box->ops);
422 context = init_listbox_context(box, term, item, NULL);
423 if (!context) return EVENT_PROCESSED;
425 msg = box->ops->get_info(item, term);
426 if (!msg) {
427 mem_free(context);
428 if (item->type == BI_FOLDER) {
429 info_box(term, 0, N_("Info"), ALIGN_CENTER,
430 N_("Press space to expand this folder."));
432 return EVENT_PROCESSED;
435 box->ops->lock(item);
437 msg_box(term, getml(context, (void *) NULL), MSGBOX_FREE_TEXT /* | MSGBOX_SCROLLABLE */,
438 N_("Info"), ALIGN_LEFT,
439 msg,
440 context, 1,
441 MSG_BOX_BUTTON(N_("~OK"), done_listbox_context, B_ESC | B_ENTER));
443 return EVENT_PROCESSED;
447 /* Goto action */
449 static void recursively_goto_each_listbox(struct session *ses,
450 struct listbox_item *root,
451 struct listbox_data *box);
453 static void
454 recursively_goto_listbox(struct session *ses, struct listbox_item *item,
455 struct listbox_data *box)
457 if (item->type == BI_FOLDER) {
458 recursively_goto_each_listbox(ses, item, box);
459 return;
461 } else if (item->type == BI_LEAF) {
462 struct uri *uri = box->ops->get_uri(item);
464 if (!uri) return;
466 open_uri_in_new_tab(ses, uri, 1, 0);
467 done_uri(uri);
471 static void
472 recursively_goto_each_listbox(struct session *ses, struct listbox_item *root,
473 struct listbox_data *box)
475 struct listbox_item *item;
477 foreach (item, root->child) {
478 recursively_goto_listbox(ses, item, box);
482 static int
483 goto_marked(struct listbox_item *item, void *data_, int *offset)
485 struct listbox_context *context = data_;
487 if (item->marked) {
488 struct session *ses = context->dlg_data->dlg->udata;
489 struct listbox_data *box = context->box;
491 recursively_goto_listbox(ses, item, box);
494 return 0;
497 widget_handler_status_T
498 push_hierbox_goto_button(struct dialog_data *dlg_data,
499 struct widget_data *button)
501 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
502 struct listbox_item *item = box->sel;
503 struct session *ses = dlg_data->dlg->udata;
504 struct terminal *term = dlg_data->win->term;
505 struct listbox_context *context;
507 if (!item) return EVENT_PROCESSED;
509 context = init_listbox_context(box, term, item, scan_for_marks);
510 if (!context) return EVENT_PROCESSED;
512 if (!context->item) {
513 context->dlg_data = dlg_data;
514 traverse_listbox_items_list(context->box->items->next,
515 context->box, 0, 0,
516 goto_marked, context);
518 } else if (item->type == BI_FOLDER) {
519 recursively_goto_each_listbox(ses, item, box);
521 } else if (item->type == BI_LEAF) {
522 struct uri *uri = box->ops->get_uri(item);
524 if (uri) {
525 goto_uri(ses, uri);
526 done_uri(uri);
529 } else {
530 mem_free(context);
531 return EVENT_PROCESSED;
534 mem_free(context);
536 /* Close the dialog */
537 delete_window(dlg_data->win);
538 return EVENT_PROCESSED;
542 /* Delete action */
544 enum delete_error {
545 DELETE_IMPOSSIBLE = 0,
546 DELETE_LOCKED,
547 DELETE_ERRORS,
550 static const struct listbox_ops_messages default_listbox_ops_messages = {
551 /* cant_delete_item */
552 N_("Sorry, but the item \"%s\" cannot be deleted."),
554 /* cant_delete_used_item */
555 N_("Sorry, but the item \"%s\" is being used by something else."),
557 /* cant_delete_folder */
558 N_("Sorry, but the folder \"%s\" cannot be deleted."),
560 /* cant_delete_used_folder */
561 N_("Sorry, but the folder \"%s\" is being used by something else."),
563 /* delete_marked_items_title */
564 N_("Delete marked items"),
566 /* delete_marked_items */
567 N_("Delete marked items?"),
569 /* delete_folder_title */
570 N_("Delete folder"),
572 /* delete_folder */
573 N_("Delete the folder \"%s\" and its content?"),
575 /* delete_item_title */
576 N_("Delete item"),
578 /* delete_item */
579 N_("Delete \"%s\"?\n\n%s"),
581 /* clear_all_items_title */
582 N_("Clear all items"),
584 /* clear_all_items */
585 N_("Do you really want to remove all items?"),
588 #define listbox_message(msg) \
589 ops->messages && ops->messages->msg \
590 ? ops->messages->msg \
591 : default_listbox_ops_messages.msg
593 static void
594 print_delete_error(struct listbox_item *item, struct terminal *term,
595 const struct listbox_ops *ops, enum delete_error err)
597 struct string msg;
598 unsigned char *errmsg;
599 unsigned char *text;
601 switch (err) {
602 case DELETE_IMPOSSIBLE:
603 if (item->type == BI_FOLDER) {
604 errmsg = listbox_message(cant_delete_folder);
605 } else {
606 errmsg = listbox_message(cant_delete_item);
608 break;
610 case DELETE_LOCKED:
611 if (item->type == BI_FOLDER) {
612 errmsg = listbox_message(cant_delete_used_folder);
613 } else {
614 errmsg = listbox_message(cant_delete_used_item);
616 break;
618 default:
619 INTERNAL("Bad delete error code (%d)!", err);
620 return;
623 text = ops->get_text(item, term);
625 if (!text || !init_string(&msg)) {
626 mem_free_if(text);
627 return;
630 add_format_to_string(&msg, _(errmsg, term), text);
631 mem_free(text);
633 if (item->type == BI_LEAF) {
634 unsigned char *info = ops->get_info(item, term);
636 if (info) {
637 add_format_to_string(&msg, "\n\n%s", info);
638 mem_free(info);
642 info_box(term, MSGBOX_FREE_TEXT, N_("Delete error"), ALIGN_LEFT,
643 msg.source);
646 static void
647 do_delete_item(struct listbox_item *item, struct listbox_context *info,
648 int last)
650 const struct listbox_ops *ops = info->box->ops;
652 assert(item);
654 if (!ops->can_delete(item)) {
655 print_delete_error(item, info->term, ops, DELETE_IMPOSSIBLE);
656 return;
659 if (ops->is_used(item)) {
660 print_delete_error(item, info->term, ops, DELETE_LOCKED);
661 return;
664 ops->delete(item, last);
667 static int
668 delete_marked(struct listbox_item *item, void *data_, int *offset)
670 struct listbox_context *context = data_;
672 if (item->marked && !context->box->ops->is_used(item)) {
673 /* Save the first marked so it can be deleted last */
674 if (!context->item) {
675 context->item = item;
676 } else {
677 do_delete_item(item, context, 0);
680 return 1;
683 return 0;
686 static void
687 push_ok_delete_button(void *context_)
689 struct listbox_context *context = context_;
690 struct listbox_item *root;
691 int last = 0;
693 if (context->item) {
694 context->box->ops->unlock(context->item);
695 } else {
696 traverse_listbox_items_list(context->box->items->next,
697 context->box, 0, 0,
698 delete_marked, context);
699 if (!context->item) return;
702 root = context->box->ops->get_root(context->item);
703 if (root) {
704 last = context->item == root->child.prev;
707 /* Delete the last one (traversal should save one to delete) */
708 do_delete_item(context->item, context, 1);
710 /* If removing the last item in a folder move focus to previous item in
711 * the folder or the root. */
712 if (last)
713 listbox_sel_move(context->widget_data, -1);
716 static widget_handler_status_T
717 query_delete_selected_item(void *context_)
719 /* [gettext_accelerator_context(query_delete_selected_item)] */
720 struct listbox_context *context, *oldcontext = context_;
721 struct terminal *term = oldcontext->term;
722 struct listbox_data *box = oldcontext->box;
723 const struct listbox_ops *ops = box->ops;
724 struct listbox_item *item = box->sel;
725 unsigned char *text;
726 enum delete_error delete;
728 assert(item);
730 delete = ops->can_delete(item) ? DELETE_LOCKED : DELETE_IMPOSSIBLE;
732 if (delete == DELETE_IMPOSSIBLE || ops->is_used(item)) {
733 print_delete_error(item, term, ops, delete);
734 return EVENT_PROCESSED;
737 context = init_listbox_context(box, term, item, NULL);
738 if (!context) return EVENT_PROCESSED;
740 context->widget_data = oldcontext->widget_data;
742 text = ops->get_text(item, term);
743 if (!text) {
744 mem_free(context);
745 return EVENT_PROCESSED;
748 if (item->type == BI_FOLDER) {
749 ops->lock(item);
750 msg_box(term, getml(context, (void *) NULL), MSGBOX_FREE_TEXT,
751 listbox_message(delete_folder_title), ALIGN_CENTER,
752 msg_text(term, listbox_message(delete_folder), text),
753 context, 2,
754 MSG_BOX_BUTTON(N_("~Yes"), push_ok_delete_button, B_ENTER),
755 MSG_BOX_BUTTON(N_("~No"), done_listbox_context, B_ESC));
756 } else {
757 unsigned char *msg = ops->get_info(item, term);
759 ops->lock(item);
761 msg_box(term, getml(context, (void *) NULL), MSGBOX_FREE_TEXT,
762 listbox_message(delete_item_title), ALIGN_LEFT,
763 msg_text(term, listbox_message(delete_item),
764 text, empty_string_or_(msg)),
765 context, 2,
766 MSG_BOX_BUTTON(N_("~Yes"), push_ok_delete_button, B_ENTER),
767 MSG_BOX_BUTTON(N_("~No"), done_listbox_context, B_ESC));
768 mem_free_if(msg);
770 mem_free(text);
772 return EVENT_PROCESSED;
775 static void
776 dont_delete_marked_items(void *const context_)
778 query_delete_selected_item(context_);
781 widget_handler_status_T
782 push_hierbox_delete_button(struct dialog_data *dlg_data,
783 struct widget_data *button)
785 /* [gettext_accelerator_context(push_hierbox_delete_button)] */
786 struct terminal *term = dlg_data->win->term;
787 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
788 const struct listbox_ops *ops = box->ops;
789 struct listbox_item *item = box->sel;
790 struct listbox_context *context;
792 if (!item) return EVENT_PROCESSED;
794 assert(ops && ops->can_delete && ops->delete);
796 context = init_listbox_context(box, term, item, scan_for_marks);
797 if (!context) return EVENT_PROCESSED;
799 context->widget_data = dlg_data->widgets_data;
801 if (context->item) {
802 widget_handler_status_T status;
804 status = query_delete_selected_item(context);
805 mem_free(context);
807 return status;
810 msg_box(term, getml(context, (void *) NULL), 0,
811 listbox_message(delete_marked_items_title), ALIGN_CENTER,
812 listbox_message(delete_marked_items),
813 context, 2,
814 MSG_BOX_BUTTON(N_("~Yes"), push_ok_delete_button, B_ENTER),
815 MSG_BOX_BUTTON(N_("~No"), dont_delete_marked_items, B_ESC));
817 return EVENT_PROCESSED;
822 /* Clear action */
824 static int
825 delete_unused(struct listbox_item *item, void *data_, int *offset)
827 struct listbox_context *context = data_;
829 if (context->box->ops->is_used(item)) return 0;
831 do_delete_item(item, context, 0);
832 return 1;
835 static void
836 do_clear_browser(void *context_)
838 struct listbox_context *context = context_;
840 traverse_listbox_items_list(context->box->items->next,
841 context->box, 0, 0,
842 delete_unused, context);
845 widget_handler_status_T
846 push_hierbox_clear_button(struct dialog_data *dlg_data,
847 struct widget_data *button)
849 /* [gettext_accelerator_context(push_hierbox_clear_button)] */
850 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
851 const struct listbox_ops *ops = box->ops;
852 struct terminal *term = dlg_data->win->term;
853 struct listbox_context *context;
855 if (!box->sel) return EVENT_PROCESSED;
857 assert(ops);
859 context = init_listbox_context(box, term, NULL, scan_for_used);
860 if (!context) return EVENT_PROCESSED;
862 if (context->item) {
863 /* FIXME: If the clear button should be used for browsers where
864 * not all items can be deleted scan_for_used() should also can
865 * for undeletable and we should be able to pass either delete
866 * error types. */
867 print_delete_error(context->item, term, ops, DELETE_LOCKED);
868 mem_free(context);
869 return EVENT_PROCESSED;
872 msg_box(term, getml(context, (void *) NULL), 0,
873 listbox_message(clear_all_items_title), ALIGN_CENTER,
874 listbox_message(clear_all_items),
875 context, 2,
876 MSG_BOX_BUTTON(N_("~Yes"), do_clear_browser, B_ENTER),
877 MSG_BOX_BUTTON(N_("~No"), NULL, B_ESC));
879 return EVENT_PROCESSED;
882 #undef listbox_message
885 /* Search action */
887 static int
888 scan_for_matches(struct listbox_item *item, void *info_, int *offset)
890 struct listbox_context *context = info_;
891 unsigned char *text = (unsigned char *) context->widget_data;
893 if (!*text) {
894 item->visible = 1;
895 return 0;
898 switch (context->box->ops->match(item, context->term, text)) {
899 case LISTBOX_MATCH_OK:
900 /* Mark that we have a match by setting the item to non-NULL */
901 context->item = item;
902 item->visible = 1;
903 break;
905 case LISTBOX_MATCH_NO:
906 item->visible = 0;
907 break;
909 case LISTBOX_MATCH_IMPOSSIBLE:
910 break;
913 return 0;
916 static int
917 mark_visible(struct listbox_item *item, void *xxx, int *offset)
919 item->visible = 1;
920 return 0;
924 static void
925 search_hierbox_browser(void *data, unsigned char *text)
927 struct dialog_data *dlg_data = data;
928 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
929 struct terminal *term = dlg_data->win->term;
930 struct listbox_context *context;
932 context = init_listbox_context(box, term, NULL, NULL);
933 if (!context) return;
935 /* Eeew :/ */
936 context->widget_data = (void *) text;
938 traverse_listbox_items_list(box->items->next, box, 0, 0,
939 scan_for_matches, context);
941 if (!context->item && *text) {
942 switch (get_opt_int("document.browse.search.show_not_found")) {
943 case 2:
944 info_box(term, MSGBOX_FREE_TEXT,
945 N_("Search"), ALIGN_CENTER,
946 msg_text(term,
947 N_("Search string '%s' not found"),
948 text));
949 break;
951 case 1:
952 beep_terminal(term);
954 default:
955 break;
958 traverse_listbox_items_list(box->items->next, box, 0, 0,
959 mark_visible, NULL);
962 mem_free(context);
965 widget_handler_status_T
966 push_hierbox_search_button(struct dialog_data *dlg_data,
967 struct widget_data *button)
969 struct terminal *term = dlg_data->win->term;
970 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
972 if (!box->sel) return EVENT_PROCESSED;
974 assert(box->ops->match);
976 input_dialog(term, NULL, N_("Search"), N_("Name"),
977 dlg_data, NULL,
978 MAX_STR_LEN, "", 0, 0, NULL,
979 search_hierbox_browser, NULL);
981 return EVENT_PROCESSED;