iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / bookmarks / dialogs.c
blob93d93b8357d17768305565a0d37563cfa7745aff
1 /* Bookmarks dialogs */
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE /* XXX: we _WANT_ strcasestr() ! */
5 #endif
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
11 #include <string.h>
13 #include "elinks.h"
15 #include "bfu/dialog.h"
16 #include "bfu/hierbox.h"
17 #include "bfu/listbox.h"
18 #include "bookmarks/bookmarks.h"
19 #include "bookmarks/dialogs.h"
20 #include "dialogs/edit.h"
21 #include "intl/gettext/libintl.h"
22 #include "main/object.h"
23 #include "protocol/uri.h"
24 #include "session/session.h"
25 #include "terminal/terminal.h"
26 #include "util/conv.h"
27 #include "util/error.h"
28 #include "util/memory.h"
29 #include "util/time.h"
32 /* Whether to save bookmarks after each modification of their list
33 * (add/modify/delete). */
34 #define BOOKMARKS_RESAVE 1
37 static void
38 lock_bookmark(struct listbox_item *item)
40 object_lock((struct bookmark *) item->udata);
43 static void
44 unlock_bookmark(struct listbox_item *item)
46 object_unlock((struct bookmark *) item->udata);
49 static int
50 is_bookmark_used(struct listbox_item *item)
52 return is_object_used((struct bookmark *) item->udata);
55 static unsigned char *
56 get_bookmark_text(struct listbox_item *item, struct terminal *term)
58 struct bookmark *bookmark = item->udata;
59 int utf8_cp = get_cp_index("UTF-8");
60 int term_cp = get_terminal_codepage(term);
61 struct conv_table *convert_table;
63 convert_table = get_translation_table(utf8_cp, term_cp);
64 if (!convert_table) return NULL;
66 return convert_string(convert_table,
67 bookmark->title, strlen(bookmark->title),
68 term_cp, CSM_NONE, NULL, NULL, NULL);
71 /** A callback for convert_string(). This ignores errors and can
72 * result in truncated strings if out of memory. Accordingly, the
73 * resulting string may be displayed in the UI but should not be saved
74 * to a file or given to another program. */
75 static void
76 add_converted_bytes_to_string(void *data, unsigned char *buf, int buflen)
78 struct string *string = data;
80 add_bytes_to_string(string, buf, buflen); /* ignore errors */
83 static unsigned char *
84 get_bookmark_info(struct listbox_item *item, struct terminal *term)
86 struct bookmark *bookmark = item->udata;
87 int utf8_cp = get_cp_index("UTF-8");
88 int term_cp = get_terminal_codepage(term);
89 struct conv_table *convert_table;
90 struct string info;
92 if (item->type == BI_FOLDER) return NULL;
93 convert_table = get_translation_table(utf8_cp, term_cp);
94 if (!convert_table) return NULL;
95 if (!init_string(&info)) return NULL;
97 add_format_to_string(&info, "%s: ", _("Title", term));
98 convert_string(convert_table, bookmark->title, strlen(bookmark->title),
99 term_cp, CSM_NONE, NULL,
100 add_converted_bytes_to_string, &info);
101 add_format_to_string(&info, "\n%s: ", _("URL", term));
102 convert_string(convert_table, bookmark->url, strlen(bookmark->url),
103 term_cp, CSM_NONE, NULL,
104 add_converted_bytes_to_string, &info);
106 return info.source;
109 static struct uri *
110 get_bookmark_uri(struct listbox_item *item)
112 struct bookmark *bookmark = item->udata;
114 /** @todo Bug 1066: Tell the URI layer that bookmark->url is UTF-8. */
115 return bookmark->url && *bookmark->url
116 ? get_translated_uri(bookmark->url, NULL) : NULL;
119 static struct listbox_item *
120 get_bookmark_root(struct listbox_item *item)
122 struct bookmark *bookmark = item->udata;
124 return bookmark->root ? bookmark->root->box_item : NULL;
127 static int
128 can_delete_bookmark(struct listbox_item *item)
130 return 1;
133 static void
134 delete_bookmark_item(struct listbox_item *item, int last)
136 struct bookmark *bookmark = item->udata;
138 assert(!is_object_used(bookmark));
140 delete_bookmark(bookmark);
142 #ifdef BOOKMARKS_RESAVE
143 if (last) write_bookmarks();
144 #endif
147 static struct listbox_ops_messages bookmarks_messages = {
148 /* cant_delete_item */
149 N_("Sorry, but the bookmark \"%s\" cannot be deleted."),
150 /* cant_delete_used_item */
151 N_("Sorry, but the bookmark \"%s\" is being used by something else."),
152 /* cant_delete_folder */
153 N_("Sorry, but the folder \"%s\" cannot be deleted."),
154 /* cant_delete_used_folder */
155 N_("Sorry, but the folder \"%s\" is being used by something else."),
156 /* delete_marked_items_title */
157 N_("Delete marked bookmarks"),
158 /* delete_marked_items */
159 N_("Delete marked bookmarks?"),
160 /* delete_folder_title */
161 N_("Delete folder"),
162 /* delete_folder */
163 N_("Delete the folder \"%s\" and all bookmarks in it?"),
164 /* delete_item_title */
165 N_("Delete bookmark"),
166 /* delete_item; xgettext:c-format */
167 N_("Delete this bookmark?"),
168 /* clear_all_items_title */
169 N_("Clear all bookmarks"),
170 /* clear_all_items_title */
171 N_("Do you really want to remove all bookmarks?"),
174 static const struct listbox_ops bookmarks_listbox_ops = {
175 lock_bookmark,
176 unlock_bookmark,
177 is_bookmark_used,
178 get_bookmark_text,
179 get_bookmark_info,
180 get_bookmark_uri,
181 get_bookmark_root,
182 NULL,
183 can_delete_bookmark,
184 delete_bookmark_item,
185 NULL,
186 &bookmarks_messages,
189 /****************************************************************************
190 Bookmark manager stuff.
191 ****************************************************************************/
193 /* Callback for the "add" button in the bookmark manager */
194 static widget_handler_status_T
195 push_add_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
197 launch_bm_add_doc_dialog(dlg_data->win->term, dlg_data,
198 (struct session *) dlg_data->dlg->udata);
199 return EVENT_PROCESSED;
203 static
204 void launch_bm_search_doc_dialog(struct terminal *, struct dialog_data *,
205 struct session *);
208 /* Callback for the "search" button in the bookmark manager */
209 static widget_handler_status_T
210 push_search_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
212 launch_bm_search_doc_dialog(dlg_data->win->term, dlg_data,
213 (struct session *) dlg_data->dlg->udata);
214 return EVENT_PROCESSED;
218 static void
219 move_bookmark_after_selected(struct bookmark *bookmark, struct bookmark *selected)
221 if (selected == bookmark->root
222 || !selected
223 || !selected->box_item
224 || !bookmark->box_item)
225 return;
227 del_from_list(bookmark->box_item);
228 del_from_list(bookmark);
230 add_at_pos(selected, bookmark);
231 add_at_pos(selected->box_item, bookmark->box_item);
234 /** Add a bookmark; if called from the bookmark manager, also move
235 * the bookmark to the right place and select it in the manager.
236 * And possibly save the bookmarks.
238 * @param term
239 * The terminal whose user told ELinks to add the bookmark.
240 * Currently, @a term affects only the charset interpretation
241 * of @a title and @a url. In the future, this function could
242 * also display error messages in @a term.
244 * @param dlg_data
245 * The bookmark manager dialog, or NULL if the bookmark is being
246 * added without involving the bookmark manager. If @a dlg_data
247 * is not NULL, dlg_data->win->term should be @a term.
249 * @param title
250 * The title of the new bookmark, in the encoding of @a term.
251 * Must not be NULL. "-" means add a separator.
253 * @param url
254 * The URL of the new bookmark, in the encoding of @a term. NULL
255 * or "" means add a bookmark folder, unless @a title is "-". */
256 static void
257 do_add_bookmark(struct terminal *term, struct dialog_data *dlg_data,
258 unsigned char *title, unsigned char *url)
260 int term_cp = get_terminal_codepage(term);
261 struct bookmark *bm = NULL;
262 struct bookmark *selected = NULL;
263 struct listbox_data *box = NULL;
265 if (dlg_data) {
266 box = get_dlg_listbox_data(dlg_data);
268 if (box->sel) {
269 selected = box->sel->udata;
271 if (box->sel->type == BI_FOLDER && box->sel->expanded) {
272 bm = selected;
273 } else {
274 bm = selected->root;
279 bm = add_bookmark_cp(bm, 1, term_cp, title, url);
280 if (!bm) return;
282 move_bookmark_after_selected(bm, selected);
284 #ifdef BOOKMARKS_RESAVE
285 write_bookmarks();
286 #endif
288 if (dlg_data) {
289 struct widget_data *widget_data = dlg_data->widgets_data;
291 /* We touch only the actual bookmark dialog, not all of them;
292 * that's right, right? ;-) --pasky */
293 listbox_sel(widget_data, bm->box_item);
298 /**** ADD FOLDER *****************************************************/
300 /** Add a bookmark folder. This is called when the user pushes the OK
301 * button in the input dialog that asks for the folder name.
303 * @param dlg_data
304 * The bookmark manager. Must not be NULL.
306 * @param foldername
307 * The folder name that the user typed in the input dialog.
308 * This is in the charset of the terminal. */
309 static void
310 do_add_folder(struct dialog_data *dlg_data, unsigned char *foldername)
312 do_add_bookmark(dlg_data->win->term, dlg_data, foldername, NULL);
315 /** Prepare to add a bookmark folder. This is called when the user
316 * pushes the "Add folder" button in the bookmark manager.
318 * @param dlg_data
319 * The bookmark manager. Must not be NULL.
321 * @param widget_data
322 * The "Add folder" button. */
323 static widget_handler_status_T
324 push_add_folder_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
326 input_dialog(dlg_data->win->term, NULL,
327 N_("Add folder"), N_("Folder name"),
328 dlg_data, NULL,
329 MAX_STR_LEN, NULL, 0, 0, NULL,
330 (void (*)(void *, unsigned char *)) do_add_folder,
331 NULL);
332 return EVENT_PROCESSED;
336 /**** ADD SEPARATOR **************************************************/
338 /** Add a bookmark separator. This is called when the user pushes the
339 * "Add separator" button in the bookmark manager.
341 * @param dlg_data
342 * The bookmark manager. Must not be NULL.
344 * @param widget_data
345 * The "Add separator" button. */
346 static widget_handler_status_T
347 push_add_separator_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
349 do_add_bookmark(dlg_data->win->term, dlg_data, "-", "");
350 redraw_dialog(dlg_data, 1);
351 return EVENT_PROCESSED;
355 /**** EDIT ***********************************************************/
357 /* Called when an edit is complete. */
358 static void
359 bookmark_edit_done(void *data) {
360 struct dialog *dlg = data;
361 struct bookmark *bm = (struct bookmark *) dlg->udata2;
362 struct dialog_data *parent_dlg_data = dlg->udata;
363 int term_cp = get_terminal_codepage(parent_dlg_data->win->term);
365 update_bookmark(bm, term_cp,
366 dlg->widgets[0].data, dlg->widgets[1].data);
367 object_unlock(bm);
369 #ifdef BOOKMARKS_RESAVE
370 write_bookmarks();
371 #endif
374 static void
375 bookmark_edit_cancel(struct dialog *dlg) {
376 struct bookmark *bm = (struct bookmark *) dlg->udata2;
378 object_unlock(bm);
381 /* Called when the edit button is pushed */
382 static widget_handler_status_T
383 push_edit_button(struct dialog_data *dlg_data, struct widget_data *edit_btn)
385 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
387 /* Follow the bookmark */
388 if (box->sel) {
389 struct bookmark *bm = (struct bookmark *) box->sel->udata;
390 int utf8_cp = get_cp_index("UTF-8");
391 int term_cp = get_terminal_codepage(dlg_data->win->term);
392 struct conv_table *convert_table;
394 convert_table = get_translation_table(utf8_cp, term_cp);
395 if (convert_table) {
396 unsigned char *title;
397 unsigned char *url;
399 title = convert_string(convert_table,
400 bm->title, strlen(bm->title),
401 term_cp, CSM_NONE,
402 NULL, NULL, NULL);
403 url = convert_string(convert_table,
404 bm->url, strlen(bm->url),
405 term_cp, CSM_NONE,
406 NULL, NULL, NULL);
407 if (title && url) {
408 object_lock(bm);
409 do_edit_dialog(dlg_data->win->term, 1,
410 N_("Edit bookmark"),
411 title, url,
412 (struct session *) dlg_data->dlg->udata,
413 dlg_data,
414 bookmark_edit_done,
415 bookmark_edit_cancel,
416 (void *) bm, EDIT_DLG_ADD);
418 mem_free_if(title);
419 mem_free_if(url);
423 return EVENT_PROCESSED;
427 /**** MOVE ***********************************************************/
429 static struct bookmark *move_cache_root_avoid;
431 static void
432 update_depths(struct listbox_item *parent)
434 struct listbox_item *item;
436 foreach (item, parent->child) {
437 item->depth = parent->depth + 1;
438 if (item->type == BI_FOLDER)
439 update_depths(item);
443 enum move_bookmark_flags {
444 MOVE_BOOKMARK_NONE = 0x00,
445 MOVE_BOOKMARK_MOVED = 0x01,
446 MOVE_BOOKMARK_CYCLE = 0x02
449 /* Traverse all bookmarks and move all marked items
450 * _into_ dest or, if insert_as_child is 0, _after_ dest. */
451 static enum move_bookmark_flags
452 do_move_bookmark(struct bookmark *dest, int insert_as_child,
453 LIST_OF(struct bookmark) *src, struct listbox_data *box)
455 static int move_bookmark_event_id = EVENT_NONE;
456 struct bookmark *bm, *next;
457 enum move_bookmark_flags result = MOVE_BOOKMARK_NONE;
459 set_event_id(move_bookmark_event_id, "bookmark-move");
461 foreachsafe (bm, next, *src) {
462 if (!bm->box_item->marked) {
463 /* Don't move this bookmark itself; but if
464 * it's a folder, then we'll look inside. */
465 } else if (bm == dest || bm == move_cache_root_avoid) {
466 /* Prevent moving a folder into itself. */
467 result |= MOVE_BOOKMARK_CYCLE;
468 } else {
469 struct hierbox_dialog_list_item *item;
471 result |= MOVE_BOOKMARK_MOVED;
472 bm->box_item->marked = 0;
474 trigger_event(move_bookmark_event_id, bm, dest);
476 foreach (item, bookmark_browser.dialogs) {
477 struct widget_data *widget_data;
478 struct listbox_data *box2;
480 widget_data = item->dlg_data->widgets_data;
481 box2 = get_listbox_widget_data(widget_data);
483 if (box2->top == bm->box_item)
484 listbox_sel_move(widget_data, 1);
487 del_from_list(bm->box_item);
488 del_from_list(bm);
489 if (insert_as_child) {
490 add_to_list(dest->child, bm);
491 add_to_list(dest->box_item->child, bm->box_item);
492 bm->root = dest;
493 insert_as_child = 0;
494 } else {
495 add_at_pos(dest, bm);
496 add_at_pos(dest->box_item, bm->box_item);
497 bm->root = dest->root;
500 bm->box_item->depth = bm->root
501 ? bm->root->box_item->depth + 1
502 : 0;
504 if (bm->box_item->type == BI_FOLDER)
505 update_depths(bm->box_item);
507 dest = bm;
509 /* We don't want to care about anything marked inside
510 * of the marked folder, let's move it as a whole
511 * directly. I believe that this is more intuitive.
512 * --pasky */
513 continue;
516 if (bm->box_item->type == BI_FOLDER) {
517 result |= do_move_bookmark(dest, insert_as_child,
518 &bm->child, box);
522 return result;
525 static widget_handler_status_T
526 push_move_button(struct dialog_data *dlg_data,
527 struct widget_data *blah)
529 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
530 struct bookmark *dest = NULL;
531 int insert_as_child = 0;
532 enum move_bookmark_flags result;
534 if (!box->sel) return EVENT_PROCESSED; /* nowhere to move to */
536 dest = box->sel->udata;
537 if (box->sel->type == BI_FOLDER && box->sel->expanded) {
538 insert_as_child = 1;
540 /* Avoid recursion headaches (prevents moving a folder into itself). */
541 move_cache_root_avoid = NULL;
543 struct bookmark *bm = dest->root;
545 while (bm) {
546 if (bm->box_item->marked)
547 move_cache_root_avoid = bm;
548 bm = bm->root;
552 result = do_move_bookmark(dest, insert_as_child, &bookmarks, box);
553 if (result & MOVE_BOOKMARK_MOVED) {
554 bookmarks_set_dirty();
556 #ifdef BOOKMARKS_RESAVE
557 write_bookmarks();
558 #endif
559 update_hierbox_browser(&bookmark_browser);
561 #ifndef CONFIG_SMALL
562 else if (result & MOVE_BOOKMARK_CYCLE) {
563 /* If the user also selected other bookmarks, then
564 * they have already been moved, and this box doesn't
565 * appear. */
566 info_box(dlg_data->win->term, 0,
567 N_("Cannot move folder inside itself"), ALIGN_LEFT,
568 N_("You are trying to move the marked folder inside "
569 "itself. To move the folder to a different "
570 "location select the new location before pressing "
571 "the Move button."));
572 } else {
573 info_box(dlg_data->win->term, 0,
574 N_("Nothing to move"), ALIGN_LEFT,
575 N_("To move bookmarks, first mark all the bookmarks "
576 "(or folders) you want to move. This can be done "
577 "with the Insert key if you're using the default "
578 "key-bindings. An asterisk will appear near all "
579 "marked bookmarks. Now move to where you want to "
580 "have the stuff moved to, and press the \"Move\" "
581 "button."));
583 #endif /* ndef CONFIG_SMALL */
585 return EVENT_PROCESSED;
589 /**** MANAGEMENT *****************************************************/
591 static const struct hierbox_browser_button bookmark_buttons[] = {
592 /* [gettext_accelerator_context(.bookmark_buttons)] */
593 { N_("~Goto"), push_hierbox_goto_button, 1 },
594 { N_("~Edit"), push_edit_button, 0 },
595 { N_("~Delete"), push_hierbox_delete_button, 0 },
596 { N_("~Add"), push_add_button, 0 },
597 { N_("Add se~parator"), push_add_separator_button, 0 },
598 { N_("Add ~folder"), push_add_folder_button, 0 },
599 { N_("~Move"), push_move_button, 0 },
600 { N_("~Search"), push_search_button, 1 },
601 #if 0
602 /* This one is too dangerous, so just let user delete
603 * the bookmarks file if needed. --Zas */
604 { N_("Clear"), push_hierbox_clear_button, 0 },
606 /* TODO: Would this be useful? --jonas */
607 { N_("Save"), push_save_button, 0 },
608 #endif
611 struct_hierbox_browser(
612 bookmark_browser,
613 N_("Bookmark manager"),
614 bookmark_buttons,
615 &bookmarks_listbox_ops
618 /* Builds the "Bookmark manager" dialog */
619 void
620 bookmark_manager(struct session *ses)
622 free_last_searched_bookmark();
623 bookmark_browser.expansion_callback = bookmarks_set_dirty;
624 hierbox_browser(&bookmark_browser, ses);
628 /****************************************************************************\
629 Bookmark search dialog.
630 \****************************************************************************/
633 /* Searchs a substring either in title or url fields (ignoring
634 * case). If search_title and search_url are not empty, it selects bookmarks
635 * matching the first OR the second.
637 * Perhaps another behavior could be to search bookmarks matching both
638 * (replacing OR by AND), but it would break a cool feature: when on a page,
639 * opening search dialog will have fields corresponding to that page, so
640 * pressing ok will find any bookmark with that title or url, permitting a
641 * rapid search of an already existing bookmark. --Zas */
643 struct bookmark_search_ctx {
644 unsigned char *url; /* UTF-8 */
645 unsigned char *title; /* system charset */
646 int found;
647 int offset;
648 int utf8_cp;
649 int system_cp;
652 #define NULL_BOOKMARK_SEARCH_CTX {NULL, NULL, 0, 0, -1, -1}
654 static int
655 test_search(struct listbox_item *item, void *data_, int *offset)
657 struct bookmark_search_ctx *ctx = data_;
659 if (!ctx->offset) {
660 ctx->found = 0; /* ignore possible match on first item */
661 } else {
662 struct bookmark *bm = item->udata;
664 assert(ctx->title && ctx->url);
666 ctx->found = (*ctx->url && c_strcasestr(bm->url, ctx->url));
667 if (!ctx->found && *ctx->title) {
668 /* The comparison of bookmark titles should
669 * be case-insensitive and locale-sensitive
670 * (Turkish dotless i). ELinks doesn't have
671 * such a function for UTF-8. The best we
672 * have is strcasestr, which uses the system
673 * charset. So convert bm->title to that.
674 * (ctx->title has already been converted.) */
675 struct conv_table *convert_table;
676 unsigned char *title = NULL;
678 convert_table = get_translation_table(ctx->utf8_cp,
679 ctx->system_cp);
680 if (convert_table) {
681 title = convert_string(convert_table,
682 bm->title,
683 strlen(bm->title),
684 ctx->system_cp,
685 CSM_NONE, NULL,
686 NULL, NULL);
689 if (title) {
690 ctx->found = (strcasestr(title, ctx->title)
691 != NULL);
692 mem_free(title);
694 /** @todo Tell the user that the string could
695 * not be converted. */
698 if (ctx->found) *offset = 0;
700 ctx->offset++;
702 return 0;
705 /* Last searched values. Both are in UTF-8. (The title could be kept
706 * in the system charset, but that would be a bit risky, because
707 * setlocale calls from Lua scripts can change the system charset.) */
708 static unsigned char *bm_last_searched_title = NULL;
709 static unsigned char *bm_last_searched_url = NULL;
711 void
712 free_last_searched_bookmark(void)
714 mem_free_set(&bm_last_searched_title, NULL);
715 mem_free_set(&bm_last_searched_url, NULL);
718 static int
719 memorize_last_searched_bookmark(const unsigned char *title,
720 const unsigned char *url)
722 /* Memorize last searched title */
723 mem_free_set(&bm_last_searched_title, stracpy(title));
724 if (!bm_last_searched_title) return 0;
726 /* Memorize last searched url */
727 mem_free_set(&bm_last_searched_url, stracpy(url));
728 if (!bm_last_searched_url) {
729 mem_free_set(&bm_last_searched_title, NULL);
730 return 0;
733 return 1;
736 /* Search bookmarks */
737 static void
738 bookmark_search_do(void *data)
740 struct dialog *dlg = data;
741 struct bookmark_search_ctx ctx = NULL_BOOKMARK_SEARCH_CTX;
742 struct listbox_data *box;
743 struct dialog_data *dlg_data;
744 struct conv_table *convert_table;
745 int term_cp;
746 unsigned char *url_term;
747 unsigned char *title_term;
748 unsigned char *title_utf8 = NULL;
750 assertm(dlg->udata != NULL, "Bookmark search with NULL udata in dialog");
751 if_assert_failed return;
753 dlg_data = (struct dialog_data *) dlg->udata;
754 term_cp = get_terminal_codepage(dlg_data->win->term);
755 ctx.system_cp = get_cp_index("System");
756 ctx.utf8_cp = get_cp_index("UTF-8");
758 title_term = dlg->widgets[0].data; /* need not be freed */
759 url_term = dlg->widgets[1].data; /* likewise */
761 convert_table = get_translation_table(term_cp, ctx.system_cp);
762 if (!convert_table) goto free_all;
763 ctx.title = convert_string(convert_table,
764 title_term, strlen(title_term),
765 ctx.system_cp, CSM_NONE, NULL, NULL, NULL);
766 if (!ctx.title) goto free_all;
768 convert_table = get_translation_table(term_cp, ctx.utf8_cp);
769 if (!convert_table) goto free_all;
770 ctx.url = convert_string(convert_table,
771 url_term, strlen(url_term),
772 ctx.utf8_cp, CSM_NONE, NULL, NULL, NULL);
773 if (!ctx.url) goto free_all;
774 title_utf8 = convert_string(convert_table,
775 title_term, strlen(title_term),
776 ctx.utf8_cp, CSM_NONE, NULL, NULL, NULL);
777 if (!title_utf8) goto free_all;
779 if (!memorize_last_searched_bookmark(title_utf8, ctx.url))
780 goto free_all;
782 box = get_dlg_listbox_data(dlg_data);
784 traverse_listbox_items_list(box->sel, box, 0, 0, test_search, &ctx);
785 if (!ctx.found) goto free_all;
787 listbox_sel_move(dlg_data->widgets_data, ctx.offset - 1);
789 free_all:
790 mem_free_if(ctx.title);
791 mem_free_if(ctx.url);
792 mem_free_if(title_utf8);
795 static void
796 launch_bm_search_doc_dialog(struct terminal *term,
797 struct dialog_data *parent,
798 struct session *ses)
800 unsigned char *title = NULL;
801 unsigned char *url = NULL;
803 if (bm_last_searched_title && bm_last_searched_url) {
804 int utf8_cp, term_cp;
805 struct conv_table *convert_table;
807 utf8_cp = get_cp_index("UTF-8");
808 term_cp = get_terminal_codepage(term);
810 convert_table = get_translation_table(utf8_cp, term_cp);
811 if (convert_table) {
812 title = convert_string(convert_table, bm_last_searched_title,
813 strlen(bm_last_searched_title), term_cp,
814 CSM_NONE, NULL, NULL, NULL);
815 url = convert_string(convert_table, bm_last_searched_url,
816 strlen(bm_last_searched_url), term_cp,
817 CSM_NONE, NULL, NULL, NULL);
819 if (!title || !url) {
820 mem_free_set(&title, NULL);
821 mem_free_set(&url, NULL);
825 do_edit_dialog(term, 1, N_("Search bookmarks"),
826 title, url,
827 ses, parent, bookmark_search_do, NULL, NULL,
828 EDIT_DLG_SEARCH);
830 mem_free_if(title);
831 mem_free_if(url);
836 /****************************************************************************\
837 Bookmark add dialog.
838 \****************************************************************************/
840 /* Adds the bookmark */
841 static void
842 bookmark_add_add(void *data)
844 struct dialog *dlg = data;
845 struct dialog_data *dlg_data = (struct dialog_data *) dlg->udata;
846 struct terminal *term = dlg->udata2;
848 do_add_bookmark(term, dlg_data, dlg->widgets[0].data, dlg->widgets[1].data);
851 /** Open a dialog box for adding a bookmark.
853 * @param term
854 * The terminal in which the dialog box should appear.
856 * @param parent
857 * The bookmark manager, or NULL if the user requested this action
858 * from somewhere else.
860 * @param ses
861 * If @a title or @a url is NULL, get defaults from the current
862 * document of @a ses.
864 * @param title
865 * The initial title of the new bookmark, in the encoding of @a term.
866 * NULL means use @a ses.
868 * @param url
869 * The initial URL of the new bookmark, in the encoding of @a term.
870 * NULL means use @a ses. */
871 void
872 launch_bm_add_dialog(struct terminal *term,
873 struct dialog_data *parent,
874 struct session *ses,
875 unsigned char *title,
876 unsigned char *url)
878 /* When the user eventually pushes the OK button, BFU calls
879 * bookmark_add_add() and gives it the struct dialog * as the
880 * void * parameter. However, bookmark_add_add() also needs
881 * to know the struct terminal *, and there is no way to get
882 * that from struct dialog. The other bookmark dialogs work
883 * around that by making dialog.udata point to the struct
884 * dialog_data of the bookmark manager, but the "Add bookmark"
885 * dialog can be triggered with ACT_MAIN_ADD_BOOKMARK, which
886 * does not involve the bookmark manager at all.
888 * The solution here is to save the struct terminal * in
889 * dialog.udata2, which the "Edit bookmark" dialog uses for
890 * struct bookmark *. When adding a new bookmark, we don't
891 * need a pointer to an existing one, of course. */
892 do_edit_dialog(term, 1, N_("Add bookmark"), title, url, ses,
893 parent, bookmark_add_add, NULL, term, EDIT_DLG_ADD);
896 void
897 launch_bm_add_doc_dialog(struct terminal *term,
898 struct dialog_data *parent,
899 struct session *ses)
901 launch_bm_add_dialog(term, parent, ses, NULL, NULL);
904 void
905 launch_bm_add_link_dialog(struct terminal *term,
906 struct dialog_data *parent,
907 struct session *ses)
909 unsigned char title[MAX_STR_LEN], url[MAX_STR_LEN];
911 launch_bm_add_dialog(term, parent, ses,
912 get_current_link_name(ses, title, MAX_STR_LEN),
913 get_current_link_url(ses, url, MAX_STR_LEN));
917 /****************************************************************************\
918 Bookmark tabs dialog.
919 \****************************************************************************/
921 static void
922 bookmark_terminal_tabs_ok(void *term_void, unsigned char *foldername)
924 struct terminal *const term = term_void;
925 int from_cp = get_terminal_codepage(term);
926 int to_cp = get_cp_index("UTF-8");
927 struct conv_table *convert_table;
928 unsigned char *converted;
930 convert_table = get_translation_table(from_cp, to_cp);
931 if (convert_table == NULL) return; /** @todo Report the error */
933 converted = convert_string(convert_table,
934 foldername, strlen(foldername),
935 to_cp, CSM_NONE,
936 NULL, NULL, NULL);
937 if (converted == NULL) return; /** @todo Report the error */
939 bookmark_terminal_tabs(term_void, converted);
940 mem_free(converted);
943 void
944 bookmark_terminal_tabs_dialog(struct terminal *term)
946 struct string string;
948 if (!init_string(&string)) return;
950 add_to_string(&string, _("Saved session", term));
952 #ifdef HAVE_STRFTIME
953 add_to_string(&string, " - ");
954 add_date_to_string(&string, get_opt_str("ui.date_format", NULL), NULL);
955 #endif
957 input_dialog(term, NULL,
958 N_("Bookmark tabs"), N_("Enter folder name"),
959 term, NULL,
960 MAX_STR_LEN, string.source, 0, 0, NULL,
961 bookmark_terminal_tabs_ok, NULL);
963 done_string(&string);