From 8b83456c02fbdc738bc25b244633eb27522b9119 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Thu, 15 Aug 2013 19:19:24 +0100 Subject: Beginnings of new hotlist module, implemented with new treeview. Currently it can only open, display and launch entries in an existing hotlist file. TODO: - Generate defualt hotlist when file not found. - Add hotlist saving. - Allow adding to hotlist. - Allow hotlist editing. - Allow hotlist nodes to be moved by dragging. - Allow hotlist visit data to be updated. --- desktop/Makefile | 2 +- desktop/hotlist.c | 869 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ desktop/hotlist.h | 93 ++++++ desktop/tree.c | 26 ++ desktop/tree.h | 1 + gtk/hotlist.c | 3 +- 6 files changed, 992 insertions(+), 2 deletions(-) create mode 100644 desktop/hotlist.c create mode 100644 desktop/hotlist.h diff --git a/desktop/Makefile b/desktop/Makefile index 03e9591c0..4ba2b8552 100644 --- a/desktop/Makefile +++ b/desktop/Makefile @@ -1,7 +1,7 @@ # Sources for desktop S_DESKTOP := cookie_manager.c cookies_old.c history_global_core.c hotlist_old.c knockout.c \ - mouse.c plot_style.c print.c search.c searchweb.c \ + hotlist.c mouse.c plot_style.c print.c search.c searchweb.c \ scrollbar.c sslcert_viewer.c textarea.c thumbnail.c tree.c \ tree_url_node.c version.c system_colour.c global_history.c treeview.c diff --git a/desktop/hotlist.c b/desktop/hotlist.c new file mode 100644 index 000000000..c25e7f732 --- /dev/null +++ b/desktop/hotlist.c @@ -0,0 +1,869 @@ +/* + * Copyright 2012 John-Mark Bell + * Copyright 2013 Michael Drake + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include + +#include "content/urldb.h" +#include "desktop/browser.h" +#include "desktop/hotlist.h" +#include "desktop/treeview.h" +#include "utils/corestrings.h" +#include "utils/messages.h" +#include "utils/utils.h" +#include "utils/libdom.h" +#include "utils/log.h" + +#define N_DAYS 28 +#define N_SEC_PER_DAY (60 * 60 * 24) + +enum hotlist_fields { + HL_TITLE, + HL_URL, + HL_LAST_VISIT, + HL_VISITS, + HL_FOLDER, + HL_N_FIELDS +}; + +struct hotlist_folder { + treeview_node *folder; + struct treeview_field_data data; +}; + +struct hotlist_ctx { + treeview *tree; + struct treeview_field_desc fields[HL_N_FIELDS]; + bool built; +}; +struct hotlist_ctx hl_ctx; + +struct hotlist_entry { + nsurl *url; + treeview_node *entry; + + struct treeview_field_data data[HL_N_FIELDS - 1]; +}; + + +/** + * Set a hotlist entry's data from the url_data. + * + * \param e hotlist entry to set up + * \param title Title for entry, or NULL if using title from data + * \param url_data Data associated with entry's URL + * \return NSERROR_OK on success, appropriate error otherwise + */ +static nserror hotlist_create_treeview_field_data( + struct hotlist_entry *e, const char *title, + const struct url_data *data) +{ + char buffer[16]; + const char *last_visited; + char *last_visited2; + int len; + + if (title == NULL) { + title = (data->title != NULL) ? + strdup(data->title) : + strdup(""); + } + + e->data[HL_TITLE].field = hl_ctx.fields[HL_TITLE].field; + e->data[HL_TITLE].value = title; + e->data[HL_TITLE].value_len = (e->data[HL_TITLE].value != NULL) ? + strlen(title) : 0; + + e->data[HL_URL].field = hl_ctx.fields[HL_URL].field; + e->data[HL_URL].value = nsurl_access(e->url); + e->data[HL_URL].value_len = nsurl_length(e->url); + + last_visited = ctime(&data->last_visit); + last_visited2 = strdup(last_visited); + if (last_visited2 != NULL) { + assert(last_visited2[24] == '\n'); + last_visited2[24] = '\0'; + } + + e->data[HL_LAST_VISIT].field = hl_ctx.fields[HL_LAST_VISIT].field; + e->data[HL_LAST_VISIT].value = last_visited2; + e->data[HL_LAST_VISIT].value_len = (last_visited2 != NULL) ? 24 : 0; + + len = snprintf(buffer, 16, "%u", data->visits); + if (len == 16) { + len--; + buffer[len] = '\0'; + } + + e->data[HL_VISITS].field = hl_ctx.fields[HL_VISITS].field; + e->data[HL_VISITS].value = strdup(buffer); + e->data[HL_VISITS].value_len = len; + + return NSERROR_OK; +} + +/** + * Add a hotlist entry to the treeview + * + * \param e Entry to add to treeview + * \param relation Existing node to insert as relation of, or NULL + * \param rel Folder's relationship to relation + * \return NSERROR_OK on success, or appropriate error otherwise + * + * It is assumed that the entry is unique (for its URL) in the global + * hotlist table + */ +static nserror hotlist_entry_insert(struct hotlist_entry *e, + treeview_node *relation, enum treeview_relationship rel) +{ + nserror err; + + err = treeview_create_node_entry(hl_ctx.tree, &(e->entry), + relation, rel, e->data, e, hl_ctx.built ? + TREE_CREATE_NONE : TREE_CREATE_SUPPRESS_RESIZE); + if (err != NSERROR_OK) { + return err; + } + + return NSERROR_OK; +} + + +/** + * Add an entry to the hotlist (creates the entry). + * + * If the treeview has already been created, the entry will be added to the + * treeview. Otherwise, the entry will have to be added to the treeview later. + * + * When we first create the hotlist we create it without the treeview, to + * simplfy sorting the entries. + * + * If set, 'title' must be allocated on the heap, ownership is yeilded to + * this function. + * + * \param url URL for entry to add to hotlist. + * \param title Title for entry, or NULL if using title from data + * \param data URL data for the entry + * \param relation Existing node to insert as relation of, or NULL + * \param rel Entry's relationship to relation + * \param entry Updated to new treeview entry node + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_add_entry_internal(nsurl *url, const char *title, + const struct url_data *data, treeview_node *relation, + enum treeview_relationship rel, treeview_node **entry) +{ + nserror err; + struct hotlist_entry *e; + + /* Create new local hotlist entry */ + e = malloc(sizeof(struct hotlist_entry)); + if (e == NULL) { + return NSERROR_NOMEM; + } + + e->url = nsurl_ref(url); + e->entry = NULL; + + err = hotlist_create_treeview_field_data(e, title, data); + if (err != NSERROR_OK) { + return err; + } + + err = hotlist_entry_insert(e, relation, rel); + if (err != NSERROR_OK) { + return err; + } + + *entry = e->entry; + + return NSERROR_OK; +} + + +/** + * Delete a hotlist entry + * + * This does not delete the treeview node, rather it should only be called from + * the treeview node delete event message. + * + * \param e Entry to delete + */ +static void hotlist_delete_entry_internal( + struct hotlist_entry *e) +{ + assert(e != NULL); + assert(e->entry == NULL); + + /* Destroy fields */ + free((void *)e->data[HL_TITLE].value); /* Eww */ + free((void *)e->data[HL_LAST_VISIT].value); /* Eww */ + free((void *)e->data[HL_VISITS].value); /* Eww */ + nsurl_unref(e->url); + + /* Destroy entry */ + free(e); +} + + +static nserror hotlist_tree_node_folder_cb( + struct treeview_node_msg msg, void *data) +{ + struct treeview_field_data *f = data; + + switch (msg.msg) { + case TREE_MSG_NODE_DELETE: + free((void*)f->value); + free(f); + break; + + case TREE_MSG_NODE_EDIT: + break; + + case TREE_MSG_NODE_LAUNCH: + break; + } + + return NSERROR_OK; +} +static nserror hotlist_tree_node_entry_cb( + struct treeview_node_msg msg, void *data) +{ + struct hotlist_entry *e = data; + + switch (msg.msg) { + case TREE_MSG_NODE_DELETE: + e->entry = NULL; + hotlist_delete_entry_internal(e); + break; + + case TREE_MSG_NODE_EDIT: + break; + + case TREE_MSG_NODE_LAUNCH: + { + nserror error; + struct browser_window *clone = NULL; + enum browser_window_nav_flags flags = + BROWSER_WINDOW_VERIFIABLE | + BROWSER_WINDOW_HISTORY | + BROWSER_WINDOW_TAB; + + /* TODO: Set clone window, to window that new tab appears in */ + + if (msg.data.node_launch.mouse & + (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2) || + clone == NULL) { + /* Shift or Ctrl launch, open in new window rather + * than tab. */ + flags ^= BROWSER_WINDOW_TAB; + } + + error = browser_window_create(flags, e->url, NULL, clone, NULL); + if (error != NSERROR_OK) { + warn_user(messages_get_errorcode(error), 0); + } + } + break; + } + return NSERROR_OK; +} +struct treeview_callback_table hl_tree_cb_t = { + .folder = hotlist_tree_node_folder_cb, + .entry = hotlist_tree_node_entry_cb +}; + + + +typedef struct { + treeview *tree; + treeview_node *rel; + enum treeview_relationship relshp; + bool last_was_h4; + dom_string *title; +} hotlist_load_ctx; + + +/** + * Parse an entry represented as a li. + * + * \param li DOM node for parsed li + * \param ctx Our hotlist loading context. + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_load_entry(dom_node *li, hotlist_load_ctx *ctx) +{ + dom_node *a; + dom_string *title1; + dom_string *url1; + char *title; + nsurl *url; + const struct url_data *data; + dom_exception derror; + nserror err; + + /* The li must contain an "a" element */ + a = libdom_find_first_element(li, corestring_lwc_a); + if (a == NULL) { + warn_user("TreeLoadError", "(Missing in
  • )"); + return NSERROR_NOMEM; + } + + derror = dom_node_get_text_content(a, &title1); + if (derror != DOM_NO_ERR) { + warn_user("TreeLoadError", "(No title)"); + dom_node_unref(a); + return NSERROR_NOMEM; + } + + derror = dom_element_get_attribute(a, corestring_dom_href, &url1); + if (derror != DOM_NO_ERR || url1 == NULL) { + warn_user("TreeLoadError", "(No URL)"); + dom_string_unref(title1); + dom_node_unref(a); + return NSERROR_NOMEM; + } + dom_node_unref(a); + + if (title1 != NULL) { + title = strndup(dom_string_data(title1), + dom_string_byte_length(title1)); + dom_string_unref(title1); + } else { + title = strdup(""); + } + if (title == NULL) { + warn_user("NoMemory", NULL); + dom_string_unref(url1); + return NSERROR_NOMEM; + } + + /* We're loading external input. + * This may be garbage, so attempt to normalise via nsurl + */ + + err = nsurl_create(dom_string_data(url1), &url); + dom_string_unref(url1); + + if (err != NSERROR_OK) { + LOG(("Failed normalising '%s'", dom_string_data(url1))); + + warn_user(messages_get_errorcode(err), NULL); + + free(title); + + return err; + } + + data = urldb_get_url_data(url); + if (data == NULL) { + /* No entry in database, so add one */ + urldb_add_url(url); + /* now attempt to get url data */ + data = urldb_get_url_data(url); + } + if (data == NULL) { + nsurl_unref(url); + free(title); + + return NSERROR_NOMEM; + } + + /* Make this URL persistent */ + urldb_set_url_persistence(url, true); + + /* Add the entry */ + err = hotlist_add_entry_internal(url, title, data, ctx->rel, + ctx->relshp, &ctx->rel); + nsurl_unref(url); + ctx->relshp = TREE_REL_NEXT_SIBLING; + + if (err != NSERROR_OK) { + free(title); + + return err; + } + + return NSERROR_OK; +} + + +/* + * Callback for libdom_iterate_child_elements, which dispite the namespace is + * a NetSurf function. + * + * \param node Node that is a child of the directory UL node + * \param ctx Our hotlist loading context. + */ +static nserror hotlist_load_directory_cb(dom_node *node, void *ctx); + +/** + * Parse a directory represented as a ul. + * + * \param ul DOM node for parsed ul + * \param directory directory to add this directory to + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_load_directory(dom_node *ul, hotlist_load_ctx *ctx) +{ + assert(ul != NULL); + assert(ctx != NULL); + + return libdom_iterate_child_elements(ul, + hotlist_load_directory_cb, ctx); +} + + +/* Documented above, in forward declaration */ +nserror hotlist_load_directory_cb(dom_node *node, void *ctx) +{ + /* TODO: return appropriate errors */ + hotlist_load_ctx *current_ctx = ctx; + dom_string *name; + dom_exception error; + nserror err; + + /* The ul may contain entries as a li, or directories as + * an h4 followed by a ul. Non-element nodes may be present + * (eg. text, comments), and are ignored. */ + + error = dom_node_get_node_name(node, &name); + if (error != DOM_NO_ERR || name == NULL) + return NSERROR_NOMEM; + + if (dom_string_caseless_lwc_isequal(name, corestring_lwc_li)) { + /* Entry handling */ + hotlist_load_entry(node, current_ctx); + current_ctx->last_was_h4 = false; + + } else if (dom_string_caseless_lwc_isequal(name, corestring_lwc_h4)) { + /* Directory handling, part 1: Get title from H4 */ + dom_string *title; + + error = dom_node_get_text_content(node, &title); + if (error != DOM_NO_ERR || title == NULL) { + warn_user("TreeLoadError", "(Empty

    " + "or memory exhausted.)"); + dom_string_unref(name); + return NSERROR_NOMEM; + } + + if (current_ctx->title != NULL) + dom_string_unref(current_ctx->title); + current_ctx->title = title; + current_ctx->last_was_h4 = true; + + } else if (current_ctx->last_was_h4 && + dom_string_caseless_lwc_isequal(name, + corestring_lwc_ul)) { + /* Directory handling, part 2: Make node, and handle children */ + char *title; + hotlist_load_ctx new_ctx; + struct treeview_field_data *field; + treeview_node *rel; + + title = strndup(dom_string_data(current_ctx->title), + dom_string_byte_length(current_ctx->title)); + if (title == NULL) { + dom_string_unref(name); + return NSERROR_NOMEM; + } + + /* Create the folder node's title field */ + field = malloc(sizeof(struct treeview_field_data)); + if (field == NULL) { + dom_string_unref(name); + free(title); + return NSERROR_NOMEM; + } + field->field = hl_ctx.fields[HL_FOLDER].field; + field->value = title; + field->value_len = strlen(title); + + /* Create the folder node */ + err = treeview_create_node_folder(current_ctx->tree, + &rel, current_ctx->rel, current_ctx->relshp, + field, field, hl_ctx.built ? TREE_CREATE_NONE : + TREE_CREATE_SUPPRESS_RESIZE); + current_ctx->rel = rel; + current_ctx->relshp = TREE_REL_NEXT_SIBLING; + + if (err != NSERROR_OK) { + dom_string_unref(name); + return NSERROR_NOMEM; + } + + new_ctx.tree = current_ctx->tree; + new_ctx.rel = rel; + new_ctx.relshp = TREE_REL_FIRST_CHILD; + new_ctx.last_was_h4 = false; + new_ctx.title = NULL; + + /* And load its contents */ + err = hotlist_load_directory(node, &new_ctx); + if (err != NSERROR_OK) { + dom_string_unref(name); + return NSERROR_NOMEM; + } + + if (new_ctx.title != NULL) { + dom_string_unref(new_ctx.title); + new_ctx.title = NULL; + } + current_ctx->last_was_h4 = false; + } else { + current_ctx->last_was_h4 = false; + } + + dom_string_unref(name); + + return NSERROR_OK; +} + + +/* + * Load the hotlist data from file + * + * \param path The path to load the hotlist file from, or NULL + * \param loaded Updated to true iff hotlist file loaded, else set false + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_load(const char *path, bool *loaded) +{ + dom_document *document; + dom_node *html, *body, *ul; + hotlist_load_ctx ctx; + nserror err; + + *loaded = false; + + /* Handle no path */ + if (path == NULL) { + LOG(("No hotlist file path provided.")); + return NSERROR_OK; + } + + /* Load hotlist file */ + err = libdom_parse_file(path, "iso-8859-1", &document); + if (err != NSERROR_OK) { + return err; + } + + /* Find HTML element */ + html = libdom_find_first_element((dom_node *) document, + corestring_lwc_html); + if (html == NULL) { + dom_node_unref(document); + warn_user("TreeLoadError", "( not found)"); + return NSERROR_OK; + } + + /* Find BODY element */ + body = libdom_find_first_element(html, corestring_lwc_body); + if (body == NULL) { + dom_node_unref(html); + dom_node_unref(document); + warn_user("TreeLoadError", "(... not found)"); + return NSERROR_OK; + } + + /* Find UL element */ + ul = libdom_find_first_element(body, corestring_lwc_ul); + if (ul == NULL) { + dom_node_unref(body); + dom_node_unref(html); + dom_node_unref(document); + warn_user("TreeLoadError", + "(......
      not found.)"); + return NSERROR_OK; + } + + /* Set up the hotlist loading context */ + ctx.tree = hl_ctx.tree; + ctx.rel = NULL; + ctx.relshp = TREE_REL_FIRST_CHILD; + ctx.last_was_h4 = false; + ctx.title = NULL; + + err = hotlist_load_directory(ul, &ctx); + if (err != NSERROR_OK) { + dom_node_unref(ul); + dom_node_unref(body); + dom_node_unref(html); + dom_node_unref(document); + warn_user("TreeLoadError", + "(......
        not found.)"); + return NSERROR_OK; + } + + if (ctx.title != NULL) { + dom_string_unref(ctx.title); + ctx.title = NULL; + } + + dom_node_unref(ul); + dom_node_unref(body); + dom_node_unref(html); + dom_node_unref(document); + + return NSERROR_OK; +} + + +/* + * Generate default hotlist + * + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_generate(void) +{ + /* TODO */ + return NSERROR_OK; +} + + +/* + * Save hotlist to file + * + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_export(const char *path) +{ + /* TODO */ + return NSERROR_OK; +} + + +/** + * Initialise the treeview entry feilds + * + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_initialise_entry_fields(void) +{ + int i; + const char *label; + + for (i = 0; i < HL_N_FIELDS; i++) + hl_ctx.fields[i].field = NULL; + + hl_ctx.fields[HL_TITLE].flags = TREE_FLAG_DEFAULT; + label = "TreeviewLabelTitle"; + label = messages_get(label); + if (lwc_intern_string(label, strlen(label), + &hl_ctx.fields[HL_TITLE].field) != + lwc_error_ok) { + goto error; + } + + hl_ctx.fields[HL_URL].flags = TREE_FLAG_NONE; + label = "TreeviewLabelURL"; + label = messages_get(label); + if (lwc_intern_string(label, strlen(label), + &hl_ctx.fields[HL_URL].field) != + lwc_error_ok) { + goto error; + } + + hl_ctx.fields[HL_LAST_VISIT].flags = TREE_FLAG_SHOW_NAME; + label = "TreeviewLabelLastVisit"; + label = messages_get(label); + if (lwc_intern_string(label, strlen(label), + &hl_ctx.fields[HL_LAST_VISIT].field) != + lwc_error_ok) { + goto error; + } + + hl_ctx.fields[HL_VISITS].flags = TREE_FLAG_SHOW_NAME; + label = "TreeviewLabelVisits"; + label = messages_get(label); + if (lwc_intern_string(label, strlen(label), + &hl_ctx.fields[HL_VISITS].field) != + lwc_error_ok) { + goto error; + } + + hl_ctx.fields[HL_FOLDER].flags = TREE_FLAG_DEFAULT; + label = "TreeviewLabelFolder"; + label = messages_get(label); + if (lwc_intern_string(label, strlen(label), + &hl_ctx.fields[HL_FOLDER].field) != + lwc_error_ok) { + return false; + } + + return NSERROR_OK; + +error: + for (i = 0; i < HL_N_FIELDS; i++) + if (hl_ctx.fields[i].field != NULL) + lwc_string_unref(hl_ctx.fields[i].field); + + return NSERROR_UNKNOWN; +} + + +/* + * Populate the hotlist from file, or generate default hotlist if no file + * + * \param path The path to load the hotlist file from, or NULL + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_populate(const char *path) +{ + nserror err; + bool loaded; + + /* Load hotlist file */ + err = hotlist_load(path, &loaded); + + /* Ignoring errors, since if there was an error, we want to generate + * the default hotlist anyway. */ + + if (loaded) + return NSERROR_OK; + + /* Couldn't load hotlist, generate a default one */ + err = hotlist_generate(); + if (err != NSERROR_OK) { + return err; + } + + return NSERROR_OK; +} + + +/* Exported interface, documented in hotlist.h */ +nserror hotlist_init(struct core_window_callback_table *cw_t, + void *core_window_handle, const char *path) +{ + nserror err; + + LOG(("Loading hotlist")); + + /* Init. hotlist treeview entry fields */ + err = hotlist_initialise_entry_fields(); + if (err != NSERROR_OK) { + hl_ctx.tree = NULL; + return err; + } + + /* Create the hotlist treeview */ + err = treeview_create(&hl_ctx.tree, &hl_tree_cb_t, + HL_N_FIELDS, hl_ctx.fields, + cw_t, core_window_handle, + TREEVIEW_NO_MOVES | TREEVIEW_DEL_EMPTY_DIRS); + if (err != NSERROR_OK) { + hl_ctx.tree = NULL; + return err; + } + + /* Populate the hotlist */ + err = hotlist_populate(path); + if (err != NSERROR_OK) { + return err; + } + + /* Hotlist tree is built + * We suppress the treeview height callback on entry insertion before + * the treeview is built. */ + hl_ctx.built = true; + + LOG(("Loaded hotlist")); + + return NSERROR_OK; +} + + +/* Exported interface, documented in hotlist.h */ +nserror hotlist_fini(const char *path) +{ + int i; + nserror err; + + LOG(("Finalising hotlist")); + + hl_ctx.built = false; + + /* Save the hotlist */ + err = hotlist_export(path); + if (err != NSERROR_OK) { + warn_user("Couldn't save the hotlist.", 0); + } + + /* Destroy the hotlist treeview */ + err = treeview_destroy(hl_ctx.tree); + + /* Free hotlist treeview entry fields */ + for (i = 0; i < HL_N_FIELDS; i++) + if (hl_ctx.fields[i].field != NULL) + lwc_string_unref(hl_ctx.fields[i].field); + + LOG(("Finalised hotlist")); + + return err; +} + + +/* Exported interface, documented in hotlist.h */ +nserror hotlist_add(nsurl *url) +{ + const struct url_data *data; + + /* If we don't have a hotlist at the moment, just return OK */ + if (hl_ctx.tree == NULL) + return NSERROR_OK; + + data = urldb_get_url_data(url); + if (data == NULL) { + LOG(("Can't add URL to hotlist that's not present in urldb.")); + return NSERROR_BAD_PARAMETER; + } + + /* TODO */ + //hotlist_add_entry(url, data); + + return NSERROR_OK; +} + + +/* Exported interface, documented in hotlist.h */ +void hotlist_redraw(int x, int y, struct rect *clip, + const struct redraw_context *ctx) +{ + treeview_redraw(hl_ctx.tree, x, y, clip, ctx); +} + + +/* Exported interface, documented in hotlist.h */ +void hotlist_mouse_action(browser_mouse_state mouse, int x, int y) +{ + treeview_mouse_action(hl_ctx.tree, mouse, x, y); +} + + +/* Exported interface, documented in hotlist.h */ +void hotlist_keypress(uint32_t key) +{ + treeview_keypress(hl_ctx.tree, key); +} + diff --git a/desktop/hotlist.h b/desktop/hotlist.h new file mode 100644 index 000000000..1fc37eada --- /dev/null +++ b/desktop/hotlist.h @@ -0,0 +1,93 @@ +/* + * Copyright 2013 Michael Drake + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _NETSURF_DESKTOP_HOTLIST_H_ +#define _NETSURF_DESKTOP_HOTLIST_H_ + +#include + +#include "desktop/core_window.h" +#include "utils/nsurl.h" + + +/** + * Initialise the hotlist. + * + * This opens the hotlist file, generating the hotlist data, and creates a + * treeview. If there's no hotlist file, it generates a default hotlist. + * + * This must be called before any other hotlist_* function. + * + * \param cw_t Callback table for core_window containing the treeview + * \param cw The core_window in which the treeview is shown + * \param path The path to hotlist file to load + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror hotlist_init(struct core_window_callback_table *cw_t, + void *core_window_handle, const char *path); + +/** + * Finalise the hotlist. + * + * This destroys the hotlist treeview and the hotlist module's + * internal data. After calling this if hotlist is required again, + * hotlist_init must be called. + * + * \param path The path to save hotlist to + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror hotlist_fini(const char *path); + +/** + * Add an entry to the hotlist. + * + * \param url URL for node being added + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror hotlist_add(nsurl *url); + +/** + * Redraw the hotlist. + * + * \param x X coordinate to render treeview at + * \param x Y coordinate to render treeview at + * \param clip Current clip rectangle (wrt tree origin) + * \param ctx Current redraw context + */ +void hotlist_redraw(int x, int y, struct rect *clip, + const struct redraw_context *ctx); + +/** + * Handles all kinds of mouse action + * + * \param mouse The current mouse state + * \param x X coordinate + * \param y Y coordinate + */ +void hotlist_mouse_action(browser_mouse_state mouse, int x, int y); + + +/** + * Key press handling. + * + * \param key The ucs4 character codepoint + * \return true if the keypress is dealt with, false otherwise. + */ +void hotlist_keypress(uint32_t key); + +#endif diff --git a/desktop/tree.c b/desktop/tree.c index 0f5697203..b49782a77 100644 --- a/desktop/tree.c +++ b/desktop/tree.c @@ -177,6 +177,7 @@ struct tree { #include "desktop/treeview.h" +#include "desktop/hotlist.h" #include "desktop/cookie_manager.h" #include "desktop/global_history.h" #include "desktop/sslcert_viewer.h" @@ -275,6 +276,13 @@ static bool treeview_test_init(struct tree *tree) warn_user("Couldn't init new global history.", 0); } } + if (tree->flags & TREE_HOTLIST) { + err = hotlist_init(&cw_t, (struct core_window *)tree, + tree_hotlist_path); + if (err != NSERROR_OK) { + warn_user("Couldn't init new hotlist.", 0); + } + } return true; } @@ -312,6 +320,12 @@ static bool treeview_test_fini(struct tree *tree) warn_user("Couldn't finalise cookie manager.", 0); } } + if (tree->flags & TREE_HOTLIST) { + err = hotlist_fini(tree_hotlist_path); + if (err != NSERROR_OK) { + warn_user("Couldn't finalise hotlist.", 0); + } + } if (treeview_inits == 1) treeview_fini(); @@ -348,6 +362,10 @@ static bool treeview_test_redraw(struct tree *tree, int x, int y, global_history_redraw(x, y, &clip, ctx); return true; } + if (tree->flags & TREE_HOTLIST) { + hotlist_redraw(x, y, &clip, ctx); + return true; + } return false; } @@ -372,6 +390,10 @@ static bool treeview_test_mouse_action(struct tree *tree, global_history_mouse_action(mouse, x, y); return true; } + if (tree->flags & TREE_HOTLIST) { + hotlist_mouse_action(mouse, x, y); + return true; + } return false; } @@ -395,6 +417,10 @@ static bool treeview_test_keypress(struct tree *tree, uint32_t key) global_history_keypress(key); return true; } + if (tree->flags & TREE_HOTLIST) { + hotlist_keypress(key); + return true; + } return false; } diff --git a/desktop/tree.h b/desktop/tree.h index a69a537e9..51fa67e8d 100644 --- a/desktop/tree.h +++ b/desktop/tree.h @@ -32,6 +32,7 @@ struct sslcert_session_data; struct sslcert_session_data *ssl_current_session; +const char *tree_hotlist_path; struct hlcache_handle; diff --git a/gtk/hotlist.c b/gtk/hotlist.c index 834699a1d..8a1c81570 100644 --- a/gtk/hotlist.c +++ b/gtk/hotlist.c @@ -121,6 +121,7 @@ bool nsgtk_hotlist_init(const char *glade_file_location) "hotlistDrawingArea")); + tree_hotlist_path = nsoption_charp(hotlist_path); hotlist_window = nsgtk_treeview_create(hotlist_old_get_tree_flags(), window, scrolled, drawing_area); @@ -132,7 +133,7 @@ bool nsgtk_hotlist_init(const char *glade_file_location) CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL); CONNECT(window, "hide", nsgtk_tree_window_hide, hotlist_window); - + hotlist_old_initialise(nsgtk_treeview_get_tree(hotlist_window), nsoption_charp(hotlist_path), tree_directory_icon_name); -- cgit v1.2.3