1 /* Bookmarks dialogs */
4 #define _GNU_SOURCE /* XXX: we _WANT_ strcasestr() ! */
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
38 lock_bookmark(struct listbox_item
*item
)
40 object_lock((struct bookmark
*) item
->udata
);
44 unlock_bookmark(struct listbox_item
*item
)
46 object_unlock((struct bookmark
*) item
->udata
);
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
;
60 return stracpy(bookmark
->title
);
63 static unsigned char *
64 get_bookmark_info(struct listbox_item
*item
, struct terminal
*term
)
66 struct bookmark
*bookmark
= item
->udata
;
69 if (item
->type
== BI_FOLDER
) return NULL
;
70 if (!init_string(&info
)) return NULL
;
72 add_format_to_string(&info
, "%s: %s", _("Title", term
), bookmark
->title
);
73 add_format_to_string(&info
, "\n%s: %s", _("URL", term
), bookmark
->url
);
79 get_bookmark_uri(struct listbox_item
*item
)
81 struct bookmark
*bookmark
= item
->udata
;
83 return bookmark
->url
&& *bookmark
->url
84 ? get_translated_uri(bookmark
->url
, NULL
) : NULL
;
87 static struct listbox_item
*
88 get_bookmark_root(struct listbox_item
*item
)
90 struct bookmark
*bookmark
= item
->udata
;
92 return bookmark
->root
? bookmark
->root
->box_item
: NULL
;
96 can_delete_bookmark(struct listbox_item
*item
)
102 delete_bookmark_item(struct listbox_item
*item
, int last
)
104 struct bookmark
*bookmark
= item
->udata
;
106 assert(!is_object_used(bookmark
));
108 delete_bookmark(bookmark
);
110 #ifdef BOOKMARKS_RESAVE
111 if (last
) write_bookmarks();
115 static struct listbox_ops_messages bookmarks_messages
= {
116 /* cant_delete_item */
117 N_("Sorry, but the bookmark \"%s\" cannot be deleted."),
118 /* cant_delete_used_item */
119 N_("Sorry, but the bookmark \"%s\" is being used by something else."),
120 /* cant_delete_folder */
121 N_("Sorry, but the folder \"%s\" cannot be deleted."),
122 /* cant_delete_used_folder */
123 N_("Sorry, but the folder \"%s\" is being used by something else."),
124 /* delete_marked_items_title */
125 N_("Delete marked bookmarks"),
126 /* delete_marked_items */
127 N_("Delete marked bookmarks?"),
128 /* delete_folder_title */
131 N_("Delete the folder \"%s\" and all bookmarks in it?"),
132 /* delete_item_title */
133 N_("Delete bookmark"),
134 /* delete_item; xgettext:c-format */
135 N_("Delete this bookmark?"),
136 /* clear_all_items_title */
137 N_("Clear all bookmarks"),
138 /* clear_all_items_title */
139 N_("Do you really want to remove all bookmarks?"),
142 static const struct listbox_ops bookmarks_listbox_ops
= {
152 delete_bookmark_item
,
157 /****************************************************************************
158 Bookmark manager stuff.
159 ****************************************************************************/
161 /* Callback for the "add" button in the bookmark manager */
162 static widget_handler_status_T
163 push_add_button(struct dialog_data
*dlg_data
, struct widget_data
*widget_data
)
165 launch_bm_add_doc_dialog(dlg_data
->win
->term
, dlg_data
,
166 (struct session
*) dlg_data
->dlg
->udata
);
167 return EVENT_PROCESSED
;
172 void launch_bm_search_doc_dialog(struct terminal
*, struct dialog_data
*,
176 /* Callback for the "search" button in the bookmark manager */
177 static widget_handler_status_T
178 push_search_button(struct dialog_data
*dlg_data
, struct widget_data
*widget_data
)
180 launch_bm_search_doc_dialog(dlg_data
->win
->term
, dlg_data
,
181 (struct session
*) dlg_data
->dlg
->udata
);
182 return EVENT_PROCESSED
;
187 move_bookmark_after_selected(struct bookmark
*bookmark
, struct bookmark
*selected
)
189 if (selected
== bookmark
->root
191 || !selected
->box_item
192 || !bookmark
->box_item
)
195 del_from_list(bookmark
->box_item
);
196 del_from_list(bookmark
);
198 add_at_pos(selected
, bookmark
);
199 add_at_pos(selected
->box_item
, bookmark
->box_item
);
203 do_add_bookmark(struct dialog_data
*dlg_data
, unsigned char *title
, unsigned char *url
)
205 struct bookmark
*bm
= NULL
;
206 struct bookmark
*selected
= NULL
;
207 struct listbox_data
*box
= NULL
;
210 box
= get_dlg_listbox_data(dlg_data
);
213 selected
= box
->sel
->udata
;
215 if (box
->sel
->type
== BI_FOLDER
&& box
->sel
->expanded
) {
223 bm
= add_bookmark(bm
, 1, title
, url
);
226 move_bookmark_after_selected(bm
, selected
);
228 #ifdef BOOKMARKS_RESAVE
233 struct widget_data
*widget_data
= dlg_data
->widgets_data
;
235 /* We touch only the actual bookmark dialog, not all of them;
236 * that's right, right? ;-) --pasky */
237 listbox_sel(widget_data
, bm
->box_item
);
242 /**** ADD FOLDER *****************************************************/
245 do_add_folder(struct dialog_data
*dlg_data
, unsigned char *foldername
)
247 do_add_bookmark(dlg_data
, foldername
, NULL
);
250 static widget_handler_status_T
251 push_add_folder_button(struct dialog_data
*dlg_data
, struct widget_data
*widget_data
)
253 input_dialog(dlg_data
->win
->term
, NULL
,
254 N_("Add folder"), N_("Folder name"),
256 MAX_STR_LEN
, NULL
, 0, 0, NULL
,
257 (void (*)(void *, unsigned char *)) do_add_folder
,
259 return EVENT_PROCESSED
;
263 /**** ADD SEPARATOR **************************************************/
265 static widget_handler_status_T
266 push_add_separator_button(struct dialog_data
*dlg_data
, struct widget_data
*widget_data
)
268 do_add_bookmark(dlg_data
, "-", "");
269 redraw_dialog(dlg_data
, 1);
270 return EVENT_PROCESSED
;
274 /**** EDIT ***********************************************************/
276 /* Called when an edit is complete. */
278 bookmark_edit_done(void *data
) {
279 struct dialog
*dlg
= data
;
280 struct bookmark
*bm
= (struct bookmark
*) dlg
->udata2
;
282 update_bookmark(bm
, dlg
->widgets
[0].data
, dlg
->widgets
[1].data
);
285 #ifdef BOOKMARKS_RESAVE
291 bookmark_edit_cancel(struct dialog
*dlg
) {
292 struct bookmark
*bm
= (struct bookmark
*) dlg
->udata2
;
297 /* Called when the edit button is pushed */
298 static widget_handler_status_T
299 push_edit_button(struct dialog_data
*dlg_data
, struct widget_data
*edit_btn
)
301 struct listbox_data
*box
= get_dlg_listbox_data(dlg_data
);
303 /* Follow the bookmark */
305 struct bookmark
*bm
= (struct bookmark
*) box
->sel
->udata
;
306 const unsigned char *title
= bm
->title
;
307 const unsigned char *url
= bm
->url
;
310 do_edit_dialog(dlg_data
->win
->term
, 1, N_("Edit bookmark"),
312 (struct session
*) dlg_data
->dlg
->udata
, dlg_data
,
313 bookmark_edit_done
, bookmark_edit_cancel
,
314 (void *) bm
, EDIT_DLG_ADD
);
317 return EVENT_PROCESSED
;
321 /**** MOVE ***********************************************************/
323 static struct bookmark
*move_cache_root_avoid
;
326 update_depths(struct listbox_item
*parent
)
328 struct listbox_item
*item
;
330 foreach (item
, parent
->child
) {
331 item
->depth
= parent
->depth
+ 1;
332 if (item
->type
== BI_FOLDER
)
337 enum move_bookmark_flags
{
338 MOVE_BOOKMARK_NONE
= 0x00,
339 MOVE_BOOKMARK_MOVED
= 0x01,
340 MOVE_BOOKMARK_CYCLE
= 0x02
343 /* Traverse all bookmarks and move all marked items
344 * _into_ dest or, if insert_as_child is 0, _after_ dest. */
345 static enum move_bookmark_flags
346 do_move_bookmark(struct bookmark
*dest
, int insert_as_child
,
347 LIST_OF(struct bookmark
) *src
, struct listbox_data
*box
)
349 static int move_bookmark_event_id
= EVENT_NONE
;
350 struct bookmark
*bm
, *next
;
351 enum move_bookmark_flags result
= MOVE_BOOKMARK_NONE
;
353 set_event_id(move_bookmark_event_id
, "bookmark-move");
355 foreachsafe (bm
, next
, *src
) {
356 if (!bm
->box_item
->marked
) {
357 /* Don't move this bookmark itself; but if
358 * it's a folder, then we'll look inside. */
359 } else if (bm
== dest
|| bm
== move_cache_root_avoid
) {
360 /* Prevent moving a folder into itself. */
361 result
|= MOVE_BOOKMARK_CYCLE
;
363 struct hierbox_dialog_list_item
*item
;
365 result
|= MOVE_BOOKMARK_MOVED
;
366 bm
->box_item
->marked
= 0;
368 trigger_event(move_bookmark_event_id
, bm
, dest
);
370 foreach (item
, bookmark_browser
.dialogs
) {
371 struct widget_data
*widget_data
;
372 struct listbox_data
*box2
;
374 widget_data
= item
->dlg_data
->widgets_data
;
375 box2
= get_listbox_widget_data(widget_data
);
377 if (box2
->top
== bm
->box_item
)
378 listbox_sel_move(widget_data
, 1);
381 del_from_list(bm
->box_item
);
383 if (insert_as_child
) {
384 add_to_list(dest
->child
, bm
);
385 add_to_list(dest
->box_item
->child
, bm
->box_item
);
389 add_at_pos(dest
, bm
);
390 add_at_pos(dest
->box_item
, bm
->box_item
);
391 bm
->root
= dest
->root
;
394 bm
->box_item
->depth
= bm
->root
395 ? bm
->root
->box_item
->depth
+ 1
398 if (bm
->box_item
->type
== BI_FOLDER
)
399 update_depths(bm
->box_item
);
403 /* We don't want to care about anything marked inside
404 * of the marked folder, let's move it as a whole
405 * directly. I believe that this is more intuitive.
410 if (bm
->box_item
->type
== BI_FOLDER
) {
411 result
|= do_move_bookmark(dest
, insert_as_child
,
419 static widget_handler_status_T
420 push_move_button(struct dialog_data
*dlg_data
,
421 struct widget_data
*blah
)
423 struct listbox_data
*box
= get_dlg_listbox_data(dlg_data
);
424 struct bookmark
*dest
= NULL
;
425 int insert_as_child
= 0;
426 enum move_bookmark_flags result
;
428 if (!box
->sel
) return EVENT_PROCESSED
; /* nowhere to move to */
430 dest
= box
->sel
->udata
;
431 if (box
->sel
->type
== BI_FOLDER
&& box
->sel
->expanded
) {
434 /* Avoid recursion headaches (prevents moving a folder into itself). */
435 move_cache_root_avoid
= NULL
;
437 struct bookmark
*bm
= dest
->root
;
440 if (bm
->box_item
->marked
)
441 move_cache_root_avoid
= bm
;
446 result
= do_move_bookmark(dest
, insert_as_child
, &bookmarks
, box
);
447 if (result
& MOVE_BOOKMARK_MOVED
) {
448 bookmarks_set_dirty();
450 #ifdef BOOKMARKS_RESAVE
453 update_hierbox_browser(&bookmark_browser
);
456 else if (result
& MOVE_BOOKMARK_CYCLE
) {
457 /* If the user also selected other bookmarks, then
458 * they have already been moved, and this box doesn't
460 info_box(dlg_data
->win
->term
, 0,
461 N_("Cannot move folder inside itself"), ALIGN_LEFT
,
462 N_("You are trying to move the marked folder inside "
463 "itself. To move the folder to a different "
464 "location select the new location before pressing "
465 "the Move button."));
467 info_box(dlg_data
->win
->term
, 0,
468 N_("Nothing to move"), ALIGN_LEFT
,
469 N_("To move bookmarks, first mark all the bookmarks "
470 "(or folders) you want to move. This can be done "
471 "with the Insert key if you're using the default "
472 "key-bindings. An asterisk will appear near all "
473 "marked bookmarks. Now move to where you want to "
474 "have the stuff moved to, and press the \"Move\" "
477 #endif /* ndef CONFIG_SMALL */
479 return EVENT_PROCESSED
;
483 /**** MANAGEMENT *****************************************************/
485 static const struct hierbox_browser_button bookmark_buttons
[] = {
486 /* [gettext_accelerator_context(.bookmark_buttons)] */
487 { N_("~Goto"), push_hierbox_goto_button
, 1 },
488 { N_("~Edit"), push_edit_button
, 0 },
489 { N_("~Delete"), push_hierbox_delete_button
, 0 },
490 { N_("~Add"), push_add_button
, 0 },
491 { N_("Add se~parator"), push_add_separator_button
, 0 },
492 { N_("Add ~folder"), push_add_folder_button
, 0 },
493 { N_("~Move"), push_move_button
, 0 },
494 { N_("~Search"), push_search_button
, 1 },
496 /* This one is too dangerous, so just let user delete
497 * the bookmarks file if needed. --Zas */
498 { N_("Clear"), push_hierbox_clear_button
, 0 },
500 /* TODO: Would this be useful? --jonas */
501 { N_("Save"), push_save_button
, 0 },
505 struct_hierbox_browser(
507 N_("Bookmark manager"),
509 &bookmarks_listbox_ops
512 /* Builds the "Bookmark manager" dialog */
514 bookmark_manager(struct session
*ses
)
516 free_last_searched_bookmark();
517 bookmark_browser
.expansion_callback
= bookmarks_set_dirty
;
518 hierbox_browser(&bookmark_browser
, ses
);
522 /****************************************************************************\
523 Bookmark search dialog.
524 \****************************************************************************/
527 /* Searchs a substring either in title or url fields (ignoring
528 * case). If search_title and search_url are not empty, it selects bookmarks
529 * matching the first OR the second.
531 * Perhaps another behavior could be to search bookmarks matching both
532 * (replacing OR by AND), but it would break a cool feature: when on a page,
533 * opening search dialog will have fields corresponding to that page, so
534 * pressing ok will find any bookmark with that title or url, permitting a
535 * rapid search of an already existing bookmark. --Zas */
537 struct bookmark_search_ctx
{
539 unsigned char *title
;
544 #define NULL_BOOKMARK_SEARCH_CTX {NULL, NULL, 0, 0}
547 test_search(struct listbox_item
*item
, void *data_
, int *offset
)
549 struct bookmark_search_ctx
*ctx
= data_
;
552 ctx
->found
= 0; /* ignore possible match on first item */
554 struct bookmark
*bm
= item
->udata
;
556 assert(ctx
->title
&& ctx
->url
);
558 ctx
->found
= (*ctx
->title
&& strcasestr(bm
->title
, ctx
->title
))
559 || (*ctx
->url
&& strcasestr(bm
->url
, ctx
->url
));
561 if (ctx
->found
) *offset
= 0;
568 /* Last searched values */
569 static unsigned char *bm_last_searched_title
= NULL
;
570 static unsigned char *bm_last_searched_url
= NULL
;
573 free_last_searched_bookmark(void)
575 mem_free_set(&bm_last_searched_title
, NULL
);
576 mem_free_set(&bm_last_searched_url
, NULL
);
580 memorize_last_searched_bookmark(struct bookmark_search_ctx
*ctx
)
582 /* Memorize last searched title */
583 mem_free_set(&bm_last_searched_title
, stracpy(ctx
->title
));
584 if (!bm_last_searched_title
) return 0;
586 /* Memorize last searched url */
587 mem_free_set(&bm_last_searched_url
, stracpy(ctx
->url
));
588 if (!bm_last_searched_url
) {
589 mem_free_set(&bm_last_searched_title
, NULL
);
596 /* Search bookmarks */
598 bookmark_search_do(void *data
)
600 struct dialog
*dlg
= data
;
601 struct bookmark_search_ctx ctx
= NULL_BOOKMARK_SEARCH_CTX
;
602 struct listbox_data
*box
;
603 struct dialog_data
*dlg_data
;
605 assertm(dlg
->udata
!= NULL
, "Bookmark search with NULL udata in dialog");
606 if_assert_failed
return;
608 ctx
.title
= dlg
->widgets
[0].data
;
609 ctx
.url
= dlg
->widgets
[1].data
;
611 if (!ctx
.title
|| !ctx
.url
)
614 if (!memorize_last_searched_bookmark(&ctx
))
617 dlg_data
= (struct dialog_data
*) dlg
->udata
;
618 box
= get_dlg_listbox_data(dlg_data
);
620 traverse_listbox_items_list(box
->sel
, box
, 0, 0, test_search
, &ctx
);
621 if (!ctx
.found
) return;
623 listbox_sel_move(dlg_data
->widgets_data
, ctx
.offset
- 1);
628 launch_bm_search_doc_dialog(struct terminal
*term
,
629 struct dialog_data
*parent
,
632 do_edit_dialog(term
, 1, N_("Search bookmarks"),
633 bm_last_searched_title
, bm_last_searched_url
,
634 ses
, parent
, bookmark_search_do
, NULL
, NULL
,
640 /****************************************************************************\
642 \****************************************************************************/
644 /* Adds the bookmark */
646 bookmark_add_add(void *data
)
648 struct dialog
*dlg
= data
;
649 struct dialog_data
*dlg_data
= (struct dialog_data
*) dlg
->udata
;
651 do_add_bookmark(dlg_data
, dlg
->widgets
[0].data
, dlg
->widgets
[1].data
);
655 launch_bm_add_dialog(struct terminal
*term
,
656 struct dialog_data
*parent
,
658 unsigned char *title
,
661 do_edit_dialog(term
, 1, N_("Add bookmark"), title
, url
, ses
,
662 parent
, bookmark_add_add
, NULL
, NULL
, EDIT_DLG_ADD
);
666 launch_bm_add_doc_dialog(struct terminal
*term
,
667 struct dialog_data
*parent
,
670 launch_bm_add_dialog(term
, parent
, ses
, NULL
, NULL
);
674 launch_bm_add_link_dialog(struct terminal
*term
,
675 struct dialog_data
*parent
,
678 unsigned char title
[MAX_STR_LEN
], url
[MAX_STR_LEN
];
680 launch_bm_add_dialog(term
, parent
, ses
,
681 get_current_link_name(ses
, title
, MAX_STR_LEN
),
682 get_current_link_url(ses
, url
, MAX_STR_LEN
));
686 /****************************************************************************\
687 Bookmark tabs dialog.
688 \****************************************************************************/
691 bookmark_terminal_tabs_dialog(struct terminal
*term
)
693 struct string string
;
695 if (!init_string(&string
)) return;
697 add_to_string(&string
, _("Saved session", term
));
700 add_to_string(&string
, " - ");
701 add_date_to_string(&string
, get_opt_str("ui.date_format"), NULL
);
704 input_dialog(term
, NULL
,
705 N_("Bookmark tabs"), N_("Enter folder name"),
707 MAX_STR_LEN
, string
.source
, 0, 0, NULL
,
708 (void (*)(void *, unsigned char *)) bookmark_terminal_tabs
,
711 done_string(&string
);