mirror of
https://github.com/rkd77/elinks.git
synced 2024-10-14 05:53:36 -04:00
990 lines
27 KiB
C
990 lines
27 KiB
C
/* Bookmarks dialogs */
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE /* XXX: we _WANT_ strcasestr() ! */
|
|
#endif
|
|
|
|
#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 "bookmarks/bookmarks.h"
|
|
#include "bookmarks/dialogs.h"
|
|
#include "dialogs/edit.h"
|
|
#include "intl/libintl.h"
|
|
#include "main/object.h"
|
|
#include "protocol/uri.h"
|
|
#include "session/session.h"
|
|
#include "terminal/terminal.h"
|
|
#include "util/conv.h"
|
|
#include "util/error.h"
|
|
#include "util/memory.h"
|
|
#include "util/time.h"
|
|
|
|
|
|
/* Whether to save bookmarks after each modification of their list
|
|
* (add/modify/delete). */
|
|
#define BOOKMARKS_RESAVE 1
|
|
|
|
|
|
static void
|
|
lock_bookmark(struct listbox_item *item)
|
|
{
|
|
object_lock((struct bookmark *) item->udata);
|
|
}
|
|
|
|
static void
|
|
unlock_bookmark(struct listbox_item *item)
|
|
{
|
|
object_unlock((struct bookmark *) item->udata);
|
|
}
|
|
|
|
static int
|
|
is_bookmark_used(struct listbox_item *item)
|
|
{
|
|
return is_object_used((struct bookmark *) item->udata);
|
|
}
|
|
|
|
static int with_urls = 0;
|
|
|
|
static char *
|
|
get_bookmark_text(struct listbox_item *item, struct terminal *term)
|
|
{
|
|
struct bookmark *bookmark = (struct bookmark *)item->udata;
|
|
int utf8_cp = get_cp_index("UTF-8");
|
|
int term_cp = get_terminal_codepage(term);
|
|
struct conv_table *convert_table;
|
|
struct string tmp;
|
|
char *ret;
|
|
|
|
if (!init_string(&tmp)) {
|
|
return NULL;
|
|
}
|
|
convert_table = get_translation_table(utf8_cp, term_cp);
|
|
if (!convert_table) return NULL;
|
|
|
|
add_to_string(&tmp, bookmark->title);
|
|
|
|
if (with_urls && bookmark->url && *(bookmark->url)) {
|
|
add_to_string(&tmp, " | ");
|
|
add_to_string(&tmp, bookmark->url);
|
|
}
|
|
|
|
ret = convert_string(convert_table,
|
|
tmp.source, tmp.length,
|
|
term_cp, CSM_NONE, NULL, NULL, NULL);
|
|
done_string(&tmp);
|
|
return ret;
|
|
}
|
|
|
|
/** A callback for convert_string(). This ignores errors and can
|
|
* result in truncated strings if out of memory. Accordingly, the
|
|
* resulting string may be displayed in the UI but should not be saved
|
|
* to a file or given to another program. */
|
|
static void
|
|
add_converted_bytes_to_string(void *data, char *buf, int buflen)
|
|
{
|
|
struct string *string = (struct string *)data;
|
|
|
|
add_bytes_to_string(string, buf, buflen); /* ignore errors */
|
|
}
|
|
|
|
static char *
|
|
get_bookmark_info(struct listbox_item *item, struct terminal *term)
|
|
{
|
|
struct bookmark *bookmark = (struct bookmark *)item->udata;
|
|
int utf8_cp = get_cp_index("UTF-8");
|
|
int term_cp = get_terminal_codepage(term);
|
|
struct conv_table *convert_table;
|
|
struct string info;
|
|
|
|
if (item->type == BI_FOLDER) return NULL;
|
|
convert_table = get_translation_table(utf8_cp, term_cp);
|
|
if (!convert_table) return NULL;
|
|
if (!init_string(&info)) return NULL;
|
|
|
|
add_format_to_string(&info, "%s: ", _("Title", term));
|
|
convert_string(convert_table, bookmark->title, strlen(bookmark->title),
|
|
term_cp, CSM_NONE, NULL,
|
|
add_converted_bytes_to_string, &info);
|
|
add_format_to_string(&info, "\n%s: ", _("URL", term));
|
|
convert_string(convert_table, bookmark->url, strlen(bookmark->url),
|
|
term_cp, CSM_NONE, NULL,
|
|
add_converted_bytes_to_string, &info);
|
|
|
|
return info.source;
|
|
}
|
|
|
|
static struct uri *
|
|
get_bookmark_uri(struct listbox_item *item)
|
|
{
|
|
struct bookmark *bookmark = (struct bookmark *)item->udata;
|
|
|
|
/** @todo Bug 1066: Tell the URI layer that bookmark->url is UTF-8. */
|
|
return bookmark->url && *bookmark->url
|
|
? get_translated_uri(bookmark->url, NULL) : NULL;
|
|
}
|
|
|
|
static struct listbox_item *
|
|
get_bookmark_root(struct listbox_item *item)
|
|
{
|
|
struct bookmark *bookmark = (struct bookmark *)item->udata;
|
|
|
|
return bookmark->root ? bookmark->root->box_item : NULL;
|
|
}
|
|
|
|
static int
|
|
can_delete_bookmark(struct listbox_item *item)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
delete_bookmark_item(struct listbox_item *item, int last)
|
|
{
|
|
struct bookmark *bookmark = (struct bookmark *)item->udata;
|
|
|
|
assert(!is_object_used(bookmark));
|
|
|
|
delete_bookmark(bookmark);
|
|
|
|
#ifdef BOOKMARKS_RESAVE
|
|
if (last) write_bookmarks();
|
|
#endif
|
|
}
|
|
|
|
static struct listbox_ops_messages bookmarks_messages = {
|
|
/* cant_delete_item */
|
|
N_("Sorry, but the bookmark \"%s\" cannot be deleted."),
|
|
/* cant_delete_used_item */
|
|
N_("Sorry, but the bookmark \"%s\" is being used by something else."),
|
|
/* cant_delete_folder */
|
|
N_("Sorry, but the folder \"%s\" cannot be deleted."),
|
|
/* cant_delete_used_folder */
|
|
N_("Sorry, but the folder \"%s\" is being used by something else."),
|
|
/* delete_marked_items_title */
|
|
N_("Delete marked bookmarks"),
|
|
/* delete_marked_items */
|
|
N_("Delete marked bookmarks?"),
|
|
/* delete_folder_title */
|
|
N_("Delete folder"),
|
|
/* delete_folder */
|
|
N_("Delete the folder \"%s\" and all bookmarks in it?"),
|
|
/* delete_item_title */
|
|
N_("Delete bookmark"),
|
|
/* delete_item; xgettext:c-format */
|
|
N_("Delete this bookmark?"),
|
|
/* clear_all_items_title */
|
|
N_("Clear all bookmarks"),
|
|
/* clear_all_items_title */
|
|
N_("Do you really want to remove all bookmarks?"),
|
|
};
|
|
|
|
static const struct listbox_ops bookmarks_listbox_ops = {
|
|
lock_bookmark,
|
|
unlock_bookmark,
|
|
is_bookmark_used,
|
|
get_bookmark_text,
|
|
get_bookmark_info,
|
|
get_bookmark_uri,
|
|
get_bookmark_root,
|
|
NULL,
|
|
can_delete_bookmark,
|
|
delete_bookmark_item,
|
|
NULL,
|
|
&bookmarks_messages,
|
|
};
|
|
|
|
/****************************************************************************
|
|
Bookmark manager stuff.
|
|
****************************************************************************/
|
|
|
|
/* Callback for the "add" button in the bookmark manager */
|
|
static widget_handler_status_T
|
|
push_add_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
|
|
{
|
|
launch_bm_add_doc_dialog(dlg_data->win->term, dlg_data,
|
|
(struct session *) dlg_data->dlg->udata);
|
|
return EVENT_PROCESSED;
|
|
}
|
|
|
|
|
|
static
|
|
void launch_bm_search_doc_dialog(struct terminal *, struct dialog_data *,
|
|
struct session *);
|
|
|
|
|
|
/* Callback for the "search" button in the bookmark manager */
|
|
static widget_handler_status_T
|
|
push_search_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
|
|
{
|
|
launch_bm_search_doc_dialog(dlg_data->win->term, dlg_data,
|
|
(struct session *) dlg_data->dlg->udata);
|
|
return EVENT_PROCESSED;
|
|
}
|
|
|
|
|
|
static void
|
|
move_bookmark_after_selected(struct bookmark *bookmark, struct bookmark *selected)
|
|
{
|
|
if (selected == bookmark->root
|
|
|| !selected
|
|
|| !selected->box_item
|
|
|| !bookmark->box_item)
|
|
return;
|
|
|
|
del_from_list(bookmark->box_item);
|
|
del_from_list(bookmark);
|
|
|
|
add_at_pos(selected, bookmark);
|
|
add_at_pos(selected->box_item, bookmark->box_item);
|
|
}
|
|
|
|
/** Add a bookmark; if called from the bookmark manager, also move
|
|
* the bookmark to the right place and select it in the manager.
|
|
* And possibly save the bookmarks.
|
|
*
|
|
* @param term
|
|
* The terminal whose user told ELinks to add the bookmark.
|
|
* Currently, @a term affects only the charset interpretation
|
|
* of @a title and @a url. In the future, this function could
|
|
* also display error messages in @a term.
|
|
*
|
|
* @param dlg_data
|
|
* The bookmark manager dialog, or NULL if the bookmark is being
|
|
* added without involving the bookmark manager. If @a dlg_data
|
|
* is not NULL, dlg_data->win->term should be @a term.
|
|
*
|
|
* @param title
|
|
* The title of the new bookmark, in the encoding of @a term.
|
|
* Must not be NULL. "-" means add a separator.
|
|
*
|
|
* @param url
|
|
* The URL of the new bookmark, in the encoding of @a term. NULL
|
|
* or "" means add a bookmark folder, unless @a title is "-". */
|
|
static void
|
|
do_add_bookmark(struct terminal *term, struct dialog_data *dlg_data,
|
|
const char *title, const char *url)
|
|
{
|
|
int term_cp = get_terminal_codepage(term);
|
|
struct bookmark *bm = NULL;
|
|
struct bookmark *selected = NULL;
|
|
struct listbox_data *box = NULL;
|
|
|
|
if (dlg_data) {
|
|
box = get_dlg_listbox_data(dlg_data);
|
|
|
|
if (box->sel) {
|
|
selected = (struct bookmark *)box->sel->udata;
|
|
|
|
if (box->sel->type == BI_FOLDER && box->sel->expanded) {
|
|
bm = selected;
|
|
} else {
|
|
bm = selected->root;
|
|
}
|
|
}
|
|
}
|
|
|
|
bm = add_bookmark_cp(bm, 1, term_cp, title, url);
|
|
if (!bm) return;
|
|
|
|
move_bookmark_after_selected(bm, selected);
|
|
|
|
#ifdef BOOKMARKS_RESAVE
|
|
write_bookmarks();
|
|
#endif
|
|
|
|
if (dlg_data) {
|
|
struct widget_data *widget_data = dlg_data->widgets_data;
|
|
|
|
/* We touch only the actual bookmark dialog, not all of them;
|
|
* that's right, right? ;-) --pasky */
|
|
listbox_sel(widget_data, bm->box_item);
|
|
}
|
|
}
|
|
|
|
|
|
/**** ADD FOLDER *****************************************************/
|
|
|
|
/** Add a bookmark folder. This is called when the user pushes the OK
|
|
* button in the input dialog that asks for the folder name.
|
|
*
|
|
* @param dlg_data
|
|
* The bookmark manager. Must not be NULL.
|
|
*
|
|
* @param foldername
|
|
* The folder name that the user typed in the input dialog.
|
|
* This is in the charset of the terminal. */
|
|
static void
|
|
do_add_folder(struct dialog_data *dlg_data, char *foldername)
|
|
{
|
|
do_add_bookmark(dlg_data->win->term, dlg_data, foldername, NULL);
|
|
}
|
|
|
|
/** Prepare to add a bookmark folder. This is called when the user
|
|
* pushes the "Add folder" button in the bookmark manager.
|
|
*
|
|
* @param dlg_data
|
|
* The bookmark manager. Must not be NULL.
|
|
*
|
|
* @param widget_data
|
|
* The "Add folder" button. */
|
|
static widget_handler_status_T
|
|
push_add_folder_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
|
|
{
|
|
input_dialog(dlg_data->win->term, NULL,
|
|
N_("Add folder"), N_("Folder name"),
|
|
dlg_data, NULL,
|
|
MAX_STR_LEN, NULL, 0, 0, NULL,
|
|
(void (*)(void *, char *)) do_add_folder,
|
|
NULL);
|
|
return EVENT_PROCESSED;
|
|
}
|
|
|
|
|
|
/**** ADD SEPARATOR **************************************************/
|
|
|
|
/** Add a bookmark separator. This is called when the user pushes the
|
|
* "Add separator" button in the bookmark manager.
|
|
*
|
|
* @param dlg_data
|
|
* The bookmark manager. Must not be NULL.
|
|
*
|
|
* @param widget_data
|
|
* The "Add separator" button. */
|
|
static widget_handler_status_T
|
|
push_add_separator_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
|
|
{
|
|
do_add_bookmark(dlg_data->win->term, dlg_data, "-", "");
|
|
redraw_dialog(dlg_data, 1);
|
|
return EVENT_PROCESSED;
|
|
}
|
|
|
|
|
|
/**** EDIT ***********************************************************/
|
|
|
|
/* Called when an edit is complete. */
|
|
static void
|
|
bookmark_edit_done(void *data) {
|
|
struct dialog *dlg = (struct dialog *)data;
|
|
struct bookmark *bm = (struct bookmark *) dlg->udata2;
|
|
struct dialog_data *parent_dlg_data = (struct dialog_data *)dlg->udata;
|
|
int term_cp = get_terminal_codepage(parent_dlg_data->win->term);
|
|
|
|
update_bookmark(bm, term_cp,
|
|
(char *)dlg->widgets[0].data, (char *)dlg->widgets[1].data);
|
|
object_unlock(bm);
|
|
|
|
#ifdef BOOKMARKS_RESAVE
|
|
write_bookmarks();
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
bookmark_edit_cancel(struct dialog *dlg) {
|
|
struct bookmark *bm = (struct bookmark *) dlg->udata2;
|
|
|
|
object_unlock(bm);
|
|
}
|
|
|
|
/* Called when the edit button is pushed */
|
|
static widget_handler_status_T
|
|
push_edit_button(struct dialog_data *dlg_data, struct widget_data *edit_btn)
|
|
{
|
|
struct listbox_data *box = get_dlg_listbox_data(dlg_data);
|
|
|
|
/* Follow the bookmark */
|
|
if (box->sel) {
|
|
struct bookmark *bm = (struct bookmark *) box->sel->udata;
|
|
int utf8_cp = get_cp_index("UTF-8");
|
|
int term_cp = get_terminal_codepage(dlg_data->win->term);
|
|
struct conv_table *convert_table;
|
|
|
|
convert_table = get_translation_table(utf8_cp, term_cp);
|
|
if (convert_table) {
|
|
char *title;
|
|
char *url;
|
|
|
|
title = convert_string(convert_table,
|
|
bm->title, strlen(bm->title),
|
|
term_cp, CSM_NONE,
|
|
NULL, NULL, NULL);
|
|
url = convert_string(convert_table,
|
|
bm->url, strlen(bm->url),
|
|
term_cp, CSM_NONE,
|
|
NULL, NULL, NULL);
|
|
if (title && url) {
|
|
object_lock(bm);
|
|
do_edit_dialog(dlg_data->win->term, 1,
|
|
N_("Edit bookmark"),
|
|
title, url,
|
|
(struct session *) dlg_data->dlg->udata,
|
|
dlg_data,
|
|
bookmark_edit_done,
|
|
bookmark_edit_cancel,
|
|
(void *) bm, EDIT_DLG_ADD);
|
|
}
|
|
mem_free_if(title);
|
|
mem_free_if(url);
|
|
}
|
|
}
|
|
|
|
return EVENT_PROCESSED;
|
|
}
|
|
|
|
|
|
/**** MOVE ***********************************************************/
|
|
|
|
static struct bookmark *move_cache_root_avoid;
|
|
|
|
static void
|
|
update_depths(struct listbox_item *parent)
|
|
{
|
|
struct listbox_item *item;
|
|
|
|
foreach (item, parent->child) {
|
|
item->depth = parent->depth + 1;
|
|
if (item->type == BI_FOLDER)
|
|
update_depths(item);
|
|
}
|
|
}
|
|
|
|
enum move_bookmark_flags {
|
|
MOVE_BOOKMARK_NONE = 0x00,
|
|
MOVE_BOOKMARK_MOVED = 0x01,
|
|
MOVE_BOOKMARK_CYCLE = 0x02
|
|
};
|
|
|
|
/* Traverse all bookmarks and move all marked items
|
|
* _into_ dest or, if insert_as_child is 0, _after_ dest. */
|
|
static /*enum move_bookmark_flags*/ unsigned int
|
|
do_move_bookmark(struct bookmark *dest, int insert_as_child,
|
|
LIST_OF(struct bookmark) *src, struct listbox_data *box)
|
|
{
|
|
static int move_bookmark_event_id = EVENT_NONE;
|
|
struct bookmark *bm, *next;
|
|
/*enum move_bookmark_flags*/ unsigned int result = MOVE_BOOKMARK_NONE;
|
|
|
|
set_event_id(move_bookmark_event_id, "bookmark-move");
|
|
|
|
foreachsafe (bm, next, *src) {
|
|
if (!bm->box_item->marked) {
|
|
/* Don't move this bookmark itself; but if
|
|
* it's a folder, then we'll look inside. */
|
|
} else if (bm == dest || bm == move_cache_root_avoid) {
|
|
/* Prevent moving a folder into itself. */
|
|
result |= MOVE_BOOKMARK_CYCLE;
|
|
} else {
|
|
struct hierbox_dialog_list_item *item;
|
|
|
|
result |= MOVE_BOOKMARK_MOVED;
|
|
bm->box_item->marked = 0;
|
|
|
|
trigger_event(move_bookmark_event_id, bm, dest);
|
|
|
|
foreach (item, bookmark_browser.dialogs) {
|
|
struct widget_data *widget_data;
|
|
struct listbox_data *box2;
|
|
|
|
widget_data = item->dlg_data->widgets_data;
|
|
box2 = get_listbox_widget_data(widget_data);
|
|
|
|
if (box2->top == bm->box_item)
|
|
listbox_sel_move(widget_data, 1);
|
|
}
|
|
|
|
del_from_list(bm->box_item);
|
|
del_from_list(bm);
|
|
if (insert_as_child) {
|
|
add_to_list(dest->child, bm);
|
|
add_to_list(dest->box_item->child, bm->box_item);
|
|
bm->root = dest;
|
|
insert_as_child = 0;
|
|
} else {
|
|
add_at_pos(dest, bm);
|
|
add_at_pos(dest->box_item, bm->box_item);
|
|
bm->root = dest->root;
|
|
}
|
|
|
|
bm->box_item->depth = bm->root
|
|
? bm->root->box_item->depth + 1
|
|
: 0;
|
|
|
|
if (bm->box_item->type == BI_FOLDER)
|
|
update_depths(bm->box_item);
|
|
|
|
dest = bm;
|
|
|
|
/* We don't want to care about anything marked inside
|
|
* of the marked folder, let's move it as a whole
|
|
* directly. I believe that this is more intuitive.
|
|
* --pasky */
|
|
continue;
|
|
}
|
|
|
|
if (bm->box_item->type == BI_FOLDER) {
|
|
result |= do_move_bookmark(dest, insert_as_child,
|
|
&bm->child, box);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static widget_handler_status_T
|
|
push_move_button(struct dialog_data *dlg_data,
|
|
struct widget_data *blah)
|
|
{
|
|
struct listbox_data *box = get_dlg_listbox_data(dlg_data);
|
|
struct bookmark *dest = NULL;
|
|
int insert_as_child = 0;
|
|
/*enum move_bookmark_flags*/ unsigned int result;
|
|
|
|
if (!box->sel) return EVENT_PROCESSED; /* nowhere to move to */
|
|
|
|
dest = (struct bookmark *)box->sel->udata;
|
|
if (box->sel->type == BI_FOLDER && box->sel->expanded) {
|
|
insert_as_child = 1;
|
|
}
|
|
/* Avoid recursion headaches (prevents moving a folder into itself). */
|
|
move_cache_root_avoid = NULL;
|
|
{
|
|
struct bookmark *bm = dest->root;
|
|
|
|
while (bm) {
|
|
if (bm->box_item->marked)
|
|
move_cache_root_avoid = bm;
|
|
bm = bm->root;
|
|
}
|
|
}
|
|
|
|
result = do_move_bookmark(dest, insert_as_child, &bookmarks, box);
|
|
if (result & MOVE_BOOKMARK_MOVED) {
|
|
bookmarks_set_dirty();
|
|
|
|
#ifdef BOOKMARKS_RESAVE
|
|
write_bookmarks();
|
|
#endif
|
|
update_hierbox_browser(&bookmark_browser);
|
|
}
|
|
#ifndef CONFIG_SMALL
|
|
else if (result & MOVE_BOOKMARK_CYCLE) {
|
|
/* If the user also selected other bookmarks, then
|
|
* they have already been moved, and this box doesn't
|
|
* appear. */
|
|
info_box(dlg_data->win->term, 0,
|
|
N_("Cannot move folder inside itself"), ALIGN_LEFT,
|
|
N_("You are trying to move the marked folder inside "
|
|
"itself. To move the folder to a different "
|
|
"location select the new location before pressing "
|
|
"the Move button."));
|
|
} else {
|
|
info_box(dlg_data->win->term, 0,
|
|
N_("Nothing to move"), ALIGN_LEFT,
|
|
N_("To move bookmarks, first mark all the bookmarks "
|
|
"(or folders) you want to move. This can be done "
|
|
"with the Insert key if you're using the default "
|
|
"key-bindings. An asterisk will appear near all "
|
|
"marked bookmarks. Now move to where you want to "
|
|
"have the stuff moved to, and press the \"Move\" "
|
|
"button."));
|
|
}
|
|
#endif /* ndef CONFIG_SMALL */
|
|
|
|
return EVENT_PROCESSED;
|
|
}
|
|
|
|
static widget_handler_status_T
|
|
push_toggle_display_button(struct dialog_data *dlg_data,
|
|
struct widget_data *blah)
|
|
{
|
|
with_urls = !with_urls;
|
|
redraw_dialog(dlg_data, 1);
|
|
return EVENT_PROCESSED;
|
|
}
|
|
|
|
/**** MANAGEMENT *****************************************************/
|
|
|
|
static const struct hierbox_browser_button bookmark_buttons[] = {
|
|
/* [gettext_accelerator_context(.bookmark_buttons)] */
|
|
{ N_("~Goto"), push_hierbox_goto_button, 1 },
|
|
{ N_("~Edit"), push_edit_button, 0 },
|
|
{ N_("~Delete"), push_hierbox_delete_button, 0 },
|
|
{ N_("~Toggle display"), push_toggle_display_button, 1 },
|
|
{ N_("~Add"), push_add_button, 0 },
|
|
{ N_("Add se~parator"), push_add_separator_button, 0 },
|
|
{ N_("Add ~folder"), push_add_folder_button, 0 },
|
|
{ N_("~Move"), push_move_button, 0 },
|
|
{ N_("~Search"), push_search_button, 1 },
|
|
#if 0
|
|
/* This one is too dangerous, so just let user delete
|
|
* the bookmarks file if needed. --Zas */
|
|
{ N_("Clear"), push_hierbox_clear_button, 0 },
|
|
|
|
/* TODO: Would this be useful? --jonas */
|
|
{ N_("Save"), push_save_button, 0 },
|
|
#endif
|
|
};
|
|
|
|
struct_hierbox_browser(
|
|
bookmark_browser,
|
|
N_("Bookmark manager"),
|
|
bookmark_buttons,
|
|
&bookmarks_listbox_ops
|
|
);
|
|
|
|
/* Builds the "Bookmark manager" dialog */
|
|
void
|
|
bookmark_manager(struct session *ses)
|
|
{
|
|
free_last_searched_bookmark();
|
|
bookmark_browser.expansion_callback = bookmarks_set_dirty;
|
|
hierbox_browser(&bookmark_browser, ses);
|
|
}
|
|
|
|
|
|
/****************************************************************************\
|
|
Bookmark search dialog.
|
|
\****************************************************************************/
|
|
|
|
|
|
/* Searchs a substring either in title or url fields (ignoring
|
|
* case). If search_title and search_url are not empty, it selects bookmarks
|
|
* matching the first OR the second.
|
|
*
|
|
* Perhaps another behavior could be to search bookmarks matching both
|
|
* (replacing OR by AND), but it would break a cool feature: when on a page,
|
|
* opening search dialog will have fields corresponding to that page, so
|
|
* pressing ok will find any bookmark with that title or url, permitting a
|
|
* rapid search of an already existing bookmark. --Zas */
|
|
|
|
struct bookmark_search_ctx {
|
|
char *url; /* UTF-8 */
|
|
char *title; /* system charset */
|
|
int found;
|
|
int offset;
|
|
int utf8_cp;
|
|
int system_cp;
|
|
};
|
|
|
|
#define NULL_BOOKMARK_SEARCH_CTX {NULL, NULL, 0, 0, -1, -1}
|
|
|
|
static int
|
|
test_search(struct listbox_item *item, void *data_, int *offset)
|
|
{
|
|
struct bookmark_search_ctx *ctx = (struct bookmark_search_ctx *)data_;
|
|
|
|
if (!ctx->offset) {
|
|
ctx->found = 0; /* ignore possible match on first item */
|
|
} else {
|
|
struct bookmark *bm = (struct bookmark *)item->udata;
|
|
|
|
assert(ctx->title && ctx->url);
|
|
|
|
ctx->found = (*ctx->url && c_strcasestr((const char *)bm->url, (const char *)ctx->url));
|
|
if (!ctx->found && *ctx->title) {
|
|
/* The comparison of bookmark titles should
|
|
* be case-insensitive and locale-sensitive
|
|
* (Turkish dotless i). ELinks doesn't have
|
|
* such a function for UTF-8. The best we
|
|
* have is strcasestr, which uses the system
|
|
* charset. So convert bm->title to that.
|
|
* (ctx->title has already been converted.) */
|
|
struct conv_table *convert_table;
|
|
char *title = NULL;
|
|
|
|
convert_table = get_translation_table(ctx->utf8_cp,
|
|
ctx->system_cp);
|
|
if (convert_table) {
|
|
title = convert_string(convert_table,
|
|
bm->title,
|
|
strlen(bm->title),
|
|
ctx->system_cp,
|
|
CSM_NONE, NULL,
|
|
NULL, NULL);
|
|
}
|
|
|
|
if (title) {
|
|
ctx->found = (strcasestr((const char *)title, (const char *)ctx->title)
|
|
!= NULL);
|
|
mem_free(title);
|
|
}
|
|
/** @todo Tell the user that the string could
|
|
* not be converted. */
|
|
}
|
|
|
|
if (ctx->found) *offset = 0;
|
|
}
|
|
ctx->offset++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Last searched values. Both are in UTF-8. (The title could be kept
|
|
* in the system charset, but that would be a bit risky, because
|
|
* setlocale calls from Lua scripts can change the system charset.) */
|
|
static char *bm_last_searched_title = NULL;
|
|
static char *bm_last_searched_url = NULL;
|
|
|
|
void
|
|
free_last_searched_bookmark(void)
|
|
{
|
|
mem_free_set(&bm_last_searched_title, NULL);
|
|
mem_free_set(&bm_last_searched_url, NULL);
|
|
}
|
|
|
|
static int
|
|
memorize_last_searched_bookmark(const char *title,
|
|
const char *url)
|
|
{
|
|
/* Memorize last searched title */
|
|
mem_free_set(&bm_last_searched_title, stracpy(title));
|
|
if (!bm_last_searched_title) return 0;
|
|
|
|
/* Memorize last searched url */
|
|
mem_free_set(&bm_last_searched_url, stracpy(url));
|
|
if (!bm_last_searched_url) {
|
|
mem_free_set(&bm_last_searched_title, NULL);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Search bookmarks */
|
|
static void
|
|
bookmark_search_do(void *data)
|
|
{
|
|
struct dialog *dlg = (struct dialog *)data;
|
|
struct bookmark_search_ctx ctx = NULL_BOOKMARK_SEARCH_CTX;
|
|
struct listbox_data *box;
|
|
struct dialog_data *dlg_data;
|
|
struct conv_table *convert_table;
|
|
int term_cp;
|
|
char *url_term;
|
|
char *title_term;
|
|
char *title_utf8 = NULL;
|
|
|
|
assertm(dlg->udata != NULL, "Bookmark search with NULL udata in dialog");
|
|
if_assert_failed return;
|
|
|
|
dlg_data = (struct dialog_data *) dlg->udata;
|
|
term_cp = get_terminal_codepage(dlg_data->win->term);
|
|
ctx.system_cp = get_cp_index("System");
|
|
ctx.utf8_cp = get_cp_index("UTF-8");
|
|
|
|
title_term = (char *)dlg->widgets[0].data; /* need not be freed */
|
|
url_term = (char *)dlg->widgets[1].data; /* likewise */
|
|
|
|
convert_table = get_translation_table(term_cp, ctx.system_cp);
|
|
if (!convert_table) goto free_all;
|
|
ctx.title = convert_string(convert_table,
|
|
title_term, strlen(title_term),
|
|
ctx.system_cp, CSM_NONE, NULL, NULL, NULL);
|
|
if (!ctx.title) goto free_all;
|
|
|
|
convert_table = get_translation_table(term_cp, ctx.utf8_cp);
|
|
if (!convert_table) goto free_all;
|
|
ctx.url = convert_string(convert_table,
|
|
url_term, strlen(url_term),
|
|
ctx.utf8_cp, CSM_NONE, NULL, NULL, NULL);
|
|
if (!ctx.url) goto free_all;
|
|
title_utf8 = convert_string(convert_table,
|
|
title_term, strlen(title_term),
|
|
ctx.utf8_cp, CSM_NONE, NULL, NULL, NULL);
|
|
if (!title_utf8) goto free_all;
|
|
|
|
if (!memorize_last_searched_bookmark(title_utf8, ctx.url))
|
|
goto free_all;
|
|
|
|
box = get_dlg_listbox_data(dlg_data);
|
|
|
|
traverse_listbox_items_list(box->sel, box, 0, 0, test_search, &ctx);
|
|
if (!ctx.found) goto free_all;
|
|
|
|
listbox_sel_move(dlg_data->widgets_data, ctx.offset - 1);
|
|
|
|
free_all:
|
|
mem_free_if(ctx.title);
|
|
mem_free_if(ctx.url);
|
|
mem_free_if(title_utf8);
|
|
}
|
|
|
|
static void
|
|
launch_bm_search_doc_dialog(struct terminal *term,
|
|
struct dialog_data *parent,
|
|
struct session *ses)
|
|
{
|
|
char *title = NULL;
|
|
char *url = NULL;
|
|
|
|
if (bm_last_searched_title && bm_last_searched_url) {
|
|
int utf8_cp, term_cp;
|
|
struct conv_table *convert_table;
|
|
|
|
utf8_cp = get_cp_index("UTF-8");
|
|
term_cp = get_terminal_codepage(term);
|
|
|
|
convert_table = get_translation_table(utf8_cp, term_cp);
|
|
if (convert_table) {
|
|
title = convert_string(convert_table, bm_last_searched_title,
|
|
strlen(bm_last_searched_title), term_cp,
|
|
CSM_NONE, NULL, NULL, NULL);
|
|
url = convert_string(convert_table, bm_last_searched_url,
|
|
strlen(bm_last_searched_url), term_cp,
|
|
CSM_NONE, NULL, NULL, NULL);
|
|
}
|
|
if (!title || !url) {
|
|
mem_free_set(&title, NULL);
|
|
mem_free_set(&url, NULL);
|
|
}
|
|
}
|
|
|
|
do_edit_dialog(term, 1, N_("Search bookmarks"),
|
|
title, url,
|
|
ses, parent, bookmark_search_do, NULL, NULL,
|
|
EDIT_DLG_SEARCH);
|
|
|
|
mem_free_if(title);
|
|
mem_free_if(url);
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************\
|
|
Bookmark add dialog.
|
|
\****************************************************************************/
|
|
|
|
/* Adds the bookmark */
|
|
static void
|
|
bookmark_add_add(void *data)
|
|
{
|
|
struct dialog *dlg = (struct dialog *)data;
|
|
struct dialog_data *dlg_data = (struct dialog_data *) dlg->udata;
|
|
struct terminal *term = (struct terminal *)dlg->udata2;
|
|
|
|
do_add_bookmark(term, dlg_data, (char *)dlg->widgets[0].data, (char *)dlg->widgets[1].data);
|
|
}
|
|
|
|
/** Open a dialog box for adding a bookmark.
|
|
*
|
|
* @param term
|
|
* The terminal in which the dialog box should appear.
|
|
*
|
|
* @param parent
|
|
* The bookmark manager, or NULL if the user requested this action
|
|
* from somewhere else.
|
|
*
|
|
* @param ses
|
|
* If @a title or @a url is NULL, get defaults from the current
|
|
* document of @a ses.
|
|
*
|
|
* @param title
|
|
* The initial title of the new bookmark, in the encoding of @a term.
|
|
* NULL means use @a ses.
|
|
*
|
|
* @param url
|
|
* The initial URL of the new bookmark, in the encoding of @a term.
|
|
* NULL means use @a ses. */
|
|
void
|
|
launch_bm_add_dialog(struct terminal *term,
|
|
struct dialog_data *parent,
|
|
struct session *ses,
|
|
char *title,
|
|
char *url)
|
|
{
|
|
/* When the user eventually pushes the OK button, BFU calls
|
|
* bookmark_add_add() and gives it the struct dialog * as the
|
|
* void * parameter. However, bookmark_add_add() also needs
|
|
* to know the struct terminal *, and there is no way to get
|
|
* that from struct dialog. The other bookmark dialogs work
|
|
* around that by making dialog.udata point to the struct
|
|
* dialog_data of the bookmark manager, but the "Add bookmark"
|
|
* dialog can be triggered with ACT_MAIN_ADD_BOOKMARK, which
|
|
* does not involve the bookmark manager at all.
|
|
*
|
|
* The solution here is to save the struct terminal * in
|
|
* dialog.udata2, which the "Edit bookmark" dialog uses for
|
|
* struct bookmark *. When adding a new bookmark, we don't
|
|
* need a pointer to an existing one, of course. */
|
|
do_edit_dialog(term, 1, N_("Add bookmark"), title, url, ses,
|
|
parent, bookmark_add_add, NULL, term, EDIT_DLG_ADD);
|
|
}
|
|
|
|
void
|
|
launch_bm_add_doc_dialog(struct terminal *term,
|
|
struct dialog_data *parent,
|
|
struct session *ses)
|
|
{
|
|
launch_bm_add_dialog(term, parent, ses, NULL, NULL);
|
|
}
|
|
|
|
void
|
|
launch_bm_add_link_dialog(struct terminal *term,
|
|
struct dialog_data *parent,
|
|
struct session *ses)
|
|
{
|
|
char title[MAX_STR_LEN], url[MAX_STR_LEN];
|
|
|
|
launch_bm_add_dialog(term, parent, ses,
|
|
get_current_link_name(ses, title, MAX_STR_LEN),
|
|
get_current_link_url(ses, url, MAX_STR_LEN));
|
|
}
|
|
|
|
|
|
/****************************************************************************\
|
|
Bookmark tabs dialog.
|
|
\****************************************************************************/
|
|
|
|
static void
|
|
bookmark_terminal_tabs_ok(void *term_void, char *foldername)
|
|
{
|
|
struct terminal *const term = (struct terminal *const)term_void;
|
|
int from_cp = get_terminal_codepage(term);
|
|
int to_cp = get_cp_index("UTF-8");
|
|
struct conv_table *convert_table;
|
|
char *converted;
|
|
|
|
convert_table = get_translation_table(from_cp, to_cp);
|
|
if (convert_table == NULL) return; /** @todo Report the error */
|
|
|
|
converted = convert_string(convert_table,
|
|
foldername, strlen(foldername),
|
|
to_cp, CSM_NONE,
|
|
NULL, NULL, NULL);
|
|
if (converted == NULL) return; /** @todo Report the error */
|
|
|
|
bookmark_terminal_tabs((struct terminal *)term_void, converted);
|
|
mem_free(converted);
|
|
}
|
|
|
|
void
|
|
bookmark_terminal_tabs_dialog(struct terminal *term)
|
|
{
|
|
struct string string;
|
|
|
|
if (!init_string(&string)) return;
|
|
|
|
add_to_string(&string, _("Saved session", term));
|
|
|
|
#ifdef HAVE_STRFTIME
|
|
add_to_string(&string, " - ");
|
|
add_date_to_string(&string, get_opt_str("ui.date_format", NULL), NULL);
|
|
#endif
|
|
|
|
input_dialog(term, NULL,
|
|
N_("Bookmark tabs"), N_("Enter folder name"),
|
|
term, NULL,
|
|
MAX_STR_LEN, string.source, 0, 0, NULL,
|
|
bookmark_terminal_tabs_ok, NULL);
|
|
|
|
done_string(&string);
|
|
}
|