From 6173bb0e6c3bf51cd463f7bc4f725429d9087b2b Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Tue, 5 Oct 2010 19:14:46 +0000 Subject: Merge treeview-redux to trunk svn path=/trunk/netsurf/; revision=10865 --- desktop/browser.c | 1 + desktop/cookies.c | 531 +++++++ desktop/cookies.h | 36 +- desktop/history_global_core.c | 464 ++++++ desktop/history_global_core.h | 44 + desktop/hotlist.c | 457 ++++++ desktop/hotlist.h | 54 + desktop/options.c | 363 +---- desktop/options.h | 5 +- desktop/sslcert.c | 276 ++++ desktop/sslcert.h | 43 + desktop/tree.c | 3168 +++++++++++++++++++++++++++++------------ desktop/tree.h | 281 ++-- desktop/tree_url_node.c | 846 +++++++++++ desktop/tree_url_node.h | 56 + 15 files changed, 5181 insertions(+), 1444 deletions(-) create mode 100644 desktop/cookies.c create mode 100644 desktop/history_global_core.c create mode 100644 desktop/history_global_core.h create mode 100644 desktop/hotlist.c create mode 100644 desktop/hotlist.h create mode 100644 desktop/sslcert.c create mode 100644 desktop/sslcert.h create mode 100644 desktop/tree_url_node.c create mode 100644 desktop/tree_url_node.h (limited to 'desktop') diff --git a/desktop/browser.c b/desktop/browser.c index 8c53813a6..b301b5fa3 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -46,6 +46,7 @@ #include "desktop/download.h" #include "desktop/frames.h" #include "desktop/history_core.h" +#include "desktop/hotlist.h" #include "desktop/gui.h" #include "desktop/options.h" #include "desktop/selection.h" diff --git a/desktop/cookies.c b/desktop/cookies.c new file mode 100644 index 000000000..c5dac5101 --- /dev/null +++ b/desktop/cookies.c @@ -0,0 +1,531 @@ +/* + * Copyright 2006 Richard Wilson + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +/** \file + * Cookies (implementation). + */ + +#include +#include +#include +#include +#include +#include +#include "content/content.h" +#include "content/hlcache.h" +#include "content/urldb.h" +#include "desktop/cookies.h" +#include "desktop/options.h" +#include "desktop/tree.h" +#include "utils/messages.h" +#include "utils/log.h" +#include "utils/url.h" +#include "utils/utils.h" + +/** Flags for each type of cookie tree node. */ +enum tree_element_cookie { + TREE_ELEMENT_PERSISTENT = 0x01, + TREE_ELEMENT_VERSION = 0x02, + TREE_ELEMENT_SECURE = 0x03, + TREE_ELEMENT_LAST_USED = 0x04, + TREE_ELEMENT_EXPIRES = 0x05, + TREE_ELEMENT_PATH = 0x06, + TREE_ELEMENT_DOMAIN = 0x07, + TREE_ELEMENT_COMMENT = 0x08, + TREE_ELEMENT_VALUE = 0x09, +}; + +static struct tree *cookies_tree; +static struct node *cookies_tree_root; +static bool user_delete; +static hlcache_handle *folder_icon; +static hlcache_handle *cookie_icon; + + +/** + * Find an entry in the cookie tree + * + * \param node the node to check the children of + * \param title The title to find + * \return Pointer to node, or NULL if not found + */ +static struct node *cookies_find(struct node *node, const char *title) +{ + struct node *search; + struct node_element *element; + + for (search = tree_node_get_child(node); search; + search = tree_node_get_next(search)) { + element = tree_node_find_element(search, TREE_ELEMENT_TITLE, + NULL); + if (strcmp(title, tree_node_element_get_text(element)) == 0) + return search; + } + return NULL; +} + +/** + * Callback for all cookie tree nodes. + */ +static node_callback_resp cookies_node_callback(void *user_data, struct node_msg_data *msg_data) +{ + struct node *node = msg_data->node; + struct node_element *domain, *path; + const char *domain_t, *path_t, *name_t; + char *space; + bool is_folder = tree_node_is_folder(node); + + /* we don't remove any icons here */ + if (msg_data->msg == NODE_DELETE_ELEMENT_IMG) + return NODE_CALLBACK_HANDLED; + + /* let the tree handle events other than text data removal */ + if (msg_data->msg != NODE_DELETE_ELEMENT_TXT) + return NODE_CALLBACK_NOT_HANDLED; + + /* check if it's a domain folder */ + if (is_folder) + return NODE_CALLBACK_NOT_HANDLED; + + switch (msg_data->flag) { + case TREE_ELEMENT_TITLE: + if (!user_delete) + break; + /* get the rest of the cookie data */ + domain = tree_node_find_element(node, + TREE_ELEMENT_DOMAIN, NULL); + path = tree_node_find_element(node, TREE_ELEMENT_PATH, + NULL); + + if ((domain != NULL) && + (path != NULL)) { + domain_t = tree_node_element_get_text(domain) + + strlen(messages_get( + "TreeDomain")) - 4; + space = strchr(domain_t, ' '); + if (space != NULL) + *space = '\0'; + path_t = tree_node_element_get_text(path) + + strlen(messages_get("TreePath")) + - 4; + space = strchr(path_t, ' '); + if (space != NULL) + *space = '\0'; + name_t = msg_data->data.text; + urldb_delete_cookie(domain_t, path_t, name_t); + } + break; + default: + break; + } + + free(msg_data->data.text); + + return NODE_CALLBACK_HANDLED; +} + + +/** + * Updates a tree entry for a cookie. + * + * All information is copied from the cookie_data, and as such can + * be edited and should be freed. + * + * \param node The node to update + * \param data The cookie data to use + * \return true if node updated, or false for failure + */ +static bool cookies_update_cookie_node(struct node *node, + const struct cookie_data *data) +{ + struct node_element *element; + char buffer[32]; + + assert(data != NULL); + + /* update the value text */ + element = tree_node_find_element(node, TREE_ELEMENT_VALUE, NULL); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeValue", + data->value != NULL ? + data->value : + messages_get("TreeUnused"))); + + + /* update the comment text */ + if ((data->comment != NULL) && + (strcmp(data->comment, "") != 0)) { + element = tree_node_find_element(node, TREE_ELEMENT_COMMENT, NULL); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeComment", + data->comment)); + } + + /* update domain text */ + element = tree_node_find_element(node, TREE_ELEMENT_DOMAIN, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeDomain", + data->domain, + data->domain_from_set ? + messages_get("TreeHeaders") : + "")); + + /* update path text */ + element = tree_node_find_element(node, TREE_ELEMENT_PATH, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreePath", data->path, + data->path_from_set ? + messages_get("TreeHeaders") : + "")); + + /* update expiry text */ + element = tree_node_find_element(node, TREE_ELEMENT_EXPIRES, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeExpires", + (data->expires > 0) + ? (data->expires == 1) + ? messages_get("TreeSession") + : ctime(&data->expires) + : messages_get("TreeUnknown"))); + + /* update last used text */ + element = tree_node_find_element(node, TREE_ELEMENT_LAST_USED, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeLastUsed", + (data->last_used > 0) ? + ctime(&data->last_used) : + messages_get("TreeUnknown"))); + + /* update secure text */ + element = tree_node_find_element(node, TREE_ELEMENT_SECURE, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeSecure", + data->secure ? + messages_get("Yes") : + messages_get("No"))); + + /* update version text */ + element = tree_node_find_element(node, TREE_ELEMENT_VERSION, element); + snprintf(buffer, sizeof(buffer), "TreeVersion%i", data->version); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeVersion", + messages_get(buffer))); + + /* update persistant text */ + element = tree_node_find_element(node, TREE_ELEMENT_PERSISTENT, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreePersistent", + data->no_destroy ? + messages_get("Yes") : + messages_get("No"))); + + return node; +} + +/** + * Creates an empty tree entry for a cookie, and links it into the tree. + * + * All information is copied from the cookie_data, and as such can + * be edited and should be freed. + * + * \param parent the node to link to + * \param data the cookie data to use + * \return the node created, or NULL for failure + */ +static struct node *cookies_create_cookie_node(struct node *parent, + const struct cookie_data *data) +{ + struct node *node; + char *name; + + name = strdup(data->name); + if (name == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return NULL; + } + + node = tree_create_leaf_node(cookies_tree, NULL, name, + false, false, false); + if (node == NULL) { + free(name); + return NULL; + } + + tree_set_node_user_callback(node, cookies_node_callback, NULL); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_PERSISTENT, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_VERSION, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_SECURE, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_LAST_USED, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_EXPIRES, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_PATH, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_DOMAIN, false); + + if ((data->comment) && (strcmp(data->comment, ""))) + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_COMMENT, false); + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_VALUE, false); + tree_set_node_icon(cookies_tree, node, cookie_icon); + + if (!cookies_update_cookie_node(node, data)) + { + tree_delete_node(NULL, node, false); + return NULL; + } + + tree_link_node(cookies_tree, parent, node, false); + return node; +} + + +/** + * Called when scheduled event gets fired. Actually performs the update. + */ +static void cookies_schedule_callback(void *scheduled_data) +{ + const struct cookie_data *data = scheduled_data; + struct node *node = NULL; + struct node *cookie_node = NULL; + char *domain_cp; + + assert(data != NULL); + + node = cookies_find(cookies_tree_root, data->domain); + + if (node == NULL) { + domain_cp = strdup(data->domain); + if (domain_cp == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return; + } + node = tree_create_folder_node(cookies_tree, + cookies_tree_root, domain_cp, + false, false, false); + if (node != NULL) { + tree_set_node_user_callback(node, cookies_node_callback, + NULL); + tree_set_node_icon(cookies_tree, node, folder_icon); + } + } + + if (node == NULL) + return; + + cookie_node = cookies_find(node, data->name); + if (cookie_node == NULL) + cookies_create_cookie_node(node, data); + else + cookies_update_cookie_node(cookie_node, data); + + return; +} + +/** + * Initialises cookies tree. + * + * \param data user data for the callbacks + * \param start_redraw callback function called before every redraw + * \param end_redraw callback function called after every redraw + * \return true on success, false on memory exhaustion + */ +bool cookies_initialise(struct tree *tree) +{ + + if (tree == NULL) + return false; + + folder_icon = tree_load_icon(tree_directory_icon_name); + cookie_icon = tree_load_icon(tree_content_icon_name); + + /* Create an empty tree */ + cookies_tree = tree; + cookies_tree_root = tree_get_root(cookies_tree); + + user_delete = false; + urldb_iterate_cookies(cookies_schedule_update); + tree_set_node_expanded(cookies_tree, cookies_tree_root, + true, true, true); + + return true; +} + + +/** + * Get flags with which the cookies tree should be created; + * + * \return the flags + */ +unsigned int cookies_get_tree_flags(void) +{ + return TREE_DELETE_EMPTY_DIRS; +} + + +/* exported interface documented in cookies.h */ +bool cookies_schedule_update(const struct cookie_data *data) +{ + assert(data != NULL); + assert(user_delete == false); + + schedule(100, cookies_schedule_callback, (void *)data); + + return true; +} + + +/* exported interface documented in cookies.h */ +void cookies_remove(const struct cookie_data *data) +{ + assert(data != NULL); + + schedule_remove(cookies_schedule_callback, (void *)data); +} + + +/** + * Free memory and release all other resources. + */ +void cookies_cleanup(void) +{ +} + +/* Actions to be connected to front end specific toolbars */ + +/** + * Delete nodes which are currently selected. + */ +void cookies_delete_selected(void) +{ + user_delete = true; + tree_delete_selected_nodes(cookies_tree, cookies_tree_root); + user_delete = false; +} + +/** + * Delete all nodes. + */ +void cookies_delete_all(void) +{ + bool needs_redraw = tree_get_redraw(cookies_tree); + if (needs_redraw) + tree_set_redraw(cookies_tree, false); + + user_delete = true; + tree_set_node_selected(cookies_tree, cookies_tree_root, true, true); + tree_delete_selected_nodes(cookies_tree, cookies_tree_root); + user_delete = false; + + if (needs_redraw) + tree_set_redraw(cookies_tree, true); +} + +/** + * Select all nodes in the tree. + */ +void cookies_select_all(void) +{ + tree_set_node_selected(cookies_tree, cookies_tree_root, true, true); +} + +/** + * Unselect all nodes. + */ +void cookies_clear_selection(void) +{ + tree_set_node_selected(cookies_tree, cookies_tree_root, true, false); +} + +/** + * Expand both domain and cookie nodes. + */ +void cookies_expand_all(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + true, true, true); +} + +/** + * Expand domain nodes only. + */ +void cookies_expand_domains(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + true, true, false); +} + +/** + * Expand cookie nodes only. + */ +void cookies_expand_cookies(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + true, false, true); +} + +/** + * Collapse both domain and cookie nodes. + */ +void cookies_collapse_all(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + false, true, true); +} + +/** + * Collapse domain nodes only. + */ +void cookies_collapse_domains(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + false, true, false); +} + +/** + * Collapse cookie nodes only. + */ +void cookies_collapse_cookies(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + false, false, true); +} diff --git a/desktop/cookies.h b/desktop/cookies.h index 4311957df..06278c006 100644 --- a/desktop/cookies.h +++ b/desktop/cookies.h @@ -25,8 +25,42 @@ #include +#include "desktop/tree.h" + struct cookie_data; -bool cookies_update(const char *domain, const struct cookie_data *data); +bool cookies_initialise(struct tree *tree); +unsigned int cookies_get_tree_flags(void); + +/** + * Perform cookie updates and addition. The update is only scheduled here. + * The actual update is performed in the callback function. + * + * \param data Data of cookie being updated. + * \return true (for urldb_iterate_entries) + */ +bool cookies_schedule_update(const struct cookie_data *data); + +/** + * Remove a cookie from the active set. + * The cookie is to be removed from the active set and no futher + * references made to the cookie data. + * + * \param data Data of cookie being removed. + */ +void cookies_remove(const struct cookie_data *data); + +void cookies_cleanup(void); + +void cookies_delete_selected(void); +void cookies_delete_all(void); +void cookies_select_all(void); +void cookies_clear_selection(void); +void cookies_expand_all(void); +void cookies_expand_domains(void); +void cookies_expand_cookies(void); +void cookies_collapse_all(void); +void cookies_collapse_domains(void); +void cookies_collapse_cookies(void); #endif diff --git a/desktop/history_global_core.c b/desktop/history_global_core.c new file mode 100644 index 000000000..b8cd9a5b0 --- /dev/null +++ b/desktop/history_global_core.c @@ -0,0 +1,464 @@ +/* + * Copyright 2005 Richard Wilson + * Copyright 2009 Paul Blokus + * + * 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 "content/content.h" +#include "content/hlcache.h" +#include "content/urldb.h" +#include "desktop/browser.h" +#include "desktop/history_global_core.h" +#include "desktop/plotters.h" +#include "desktop/tree.h" +#include "desktop/tree_url_node.h" + +#ifdef riscos +#include "riscos/gui.h" +#endif +#include "utils/messages.h" +#include "utils/utils.h" +#include "utils/log.h" + +#define MAXIMUM_BASE_NODES 16 +#define GLOBAL_HISTORY_RECENT_URLS 16 +#define URL_CHUNK_LENGTH 512 + +static struct node *global_history_base_node[MAXIMUM_BASE_NODES]; +static int global_history_base_node_time[MAXIMUM_BASE_NODES]; +static int global_history_base_node_count = 0; + +static bool global_history_initialised; + +static struct tree *global_history_tree; +static struct node *global_history_tree_root; + +static hlcache_handle *folder_icon; + +static const char *const weekday_msg_name [] = +{ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" +}; + +/** + * Find an entry in the global history + * + * \param url The URL to find + * \return Pointer to node, or NULL if not found + */ +static struct node *history_global_find(const char *url) +{ + int i; + struct node *node; + const char *text; + + for (i = 0; i < global_history_base_node_count; i++) { + if (!tree_node_is_deleted(global_history_base_node[i])) { + node = tree_node_get_child(global_history_base_node[i]); + for (; node != NULL; node = tree_node_get_next(node)) { + text = tree_url_node_get_url(node); + if ((text != NULL) && !strcmp(url, text)) + return node; + } + } + } + return NULL; +} + +/** + * Internal routine to actually perform global history addition + * + * \param url The URL to add + * \param data URL data associated with URL + * \return true (for urldb_iterate_entries) + */ +static bool global_history_add_internal(const char *url, + const struct url_data *data) +{ + int i, j; + struct node *parent = NULL; + struct node *link; + struct node *node; + bool before = false; + int visit_date; + + assert((url != NULL) && (data != NULL)); + + visit_date = data->last_visit; + + /* find parent node */ + for (i = 0; i < global_history_base_node_count; i++) { + if (global_history_base_node_time[i] <= visit_date) { + parent = global_history_base_node[i]; + break; + } + } + + /* the entry is too old to care about */ + if (parent == NULL) + return true; + + if (tree_node_is_deleted(parent)) { + /* parent was deleted, so find place to insert it */ + link = global_history_tree_root; + + for (j = global_history_base_node_count - 1; j >= 0; j--) { + if (!tree_node_is_deleted(global_history_base_node[j]) && + global_history_base_node_time[j] > + global_history_base_node_time[i]) { + link = global_history_base_node[j]; + before = true; + break; + } + } + + tree_set_node_selected(global_history_tree, + parent, true, false); + tree_set_node_expanded(global_history_tree, + parent, false, true, true); + tree_link_node(global_history_tree, link, parent, before); + } + + /* find any previous occurance */ + if (global_history_initialised == false) { + node = history_global_find(url); + if (node != NULL) { + tree_update_URL_node(global_history_tree, + node, url, data, true); + tree_delink_node(global_history_tree, node); + tree_link_node(global_history_tree, parent, node, + false); + return true; + } + } + + /* Add the node at the bottom */ + node = tree_create_URL_node_shared(global_history_tree, + parent, url, data, + tree_url_node_callback, NULL); + + return true; +} + +static node_callback_resp +history_global_node_callback(void *user_data, + struct node_msg_data *msg_data) +{ + if (msg_data->msg == NODE_DELETE_ELEMENT_IMG) + return NODE_CALLBACK_HANDLED; + return NODE_CALLBACK_NOT_HANDLED; +} + +/** + * Initialises a single grouping node for the global history tree. + * + * \return false on memory exhaustion, true otherwise + */ +static bool history_global_initialise_node(const char *title, + time_t base, int days_back) +{ + struct tm *full_time; + char *buffer; + struct node *node; + + base += days_back * 60 * 60 * 24; + if (title == NULL) { + full_time = localtime(&base); + buffer = strdup(messages_get(weekday_msg_name[full_time->tm_wday])); + } else { + buffer = strdup(title); + } + + if (buffer == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return false; + } + + node = tree_create_folder_node(NULL, NULL, buffer, + false, true, true); + if (node == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + free(buffer); + return false; + } + if (folder_icon != NULL) + tree_set_node_icon(global_history_tree, node, folder_icon); + tree_set_node_user_callback(node, history_global_node_callback, NULL); + + global_history_base_node[global_history_base_node_count] = node; + global_history_base_node_time[global_history_base_node_count] = base; + global_history_base_node_count++; + + return true; +} + +/** + * Initialises the grouping nodes(Today, Yesterday etc.) for the global history + * tree. + * + * \return false on memory exhaustion, true otherwise + */ +static bool history_global_initialise_nodes(void) +{ + struct tm *full_time; + time_t t; + int weekday; + int i; + + /* get the current time */ + t = time(NULL); + if (t == -1) { + LOG(("time info unaviable")); + return false; + } + + /* get the time at the start of today */ + full_time = localtime(&t); + weekday = full_time->tm_wday; + full_time->tm_sec = 0; + full_time->tm_min = 0; + full_time->tm_hour = 0; + t = mktime(full_time); + if (t == -1) { + LOG(("mktime failed")); + return false; + } + + history_global_initialise_node(messages_get("DateToday"), t, 0); + if (weekday > 0) + if (!history_global_initialise_node( + messages_get("DateYesterday"), t, -1)) + return false; + for (i = 2; i <= weekday; i++) + if (!history_global_initialise_node(NULL, t, -i)) + return false; + + if (!history_global_initialise_node(messages_get("Date1Week"), + t, -weekday - 7)) + return false; + if (!history_global_initialise_node(messages_get("Date2Week"), + t, -weekday - 14)) + return false; + if (!history_global_initialise_node(messages_get("Date3Week"), + t, -weekday - 21)) + return false; + + return true; +} + +/** + * Initialises the global history tree. + * + * \param data user data for the callbacks + * \param start_redraw callback function called before every redraw + * \param end_redraw callback function called after every redraw + * \return true on success, false on memory exhaustion + */ +bool history_global_initialise(struct tree *tree) +{ + struct node *first; + + folder_icon = tree_load_icon(tree_directory_icon_name); + tree_url_node_init(); + + if (tree == NULL) + return false; + + global_history_tree = tree; + global_history_tree_root = tree_get_root(global_history_tree); + + if (!history_global_initialise_nodes()) + return false; + + global_history_initialised = true; + urldb_iterate_entries(global_history_add_internal); + global_history_initialised = false; + tree_set_node_expanded(global_history_tree, global_history_tree_root, + false, true, true); + first = tree_node_get_child(global_history_tree_root); + if (first != NULL) + tree_set_node_expanded(global_history_tree, first, + true, false, false); + + return true; +} + + +/** + * Get flags with which the global history tree should be created; + * + * \return the flags + */ +unsigned int history_global_get_tree_flags(void) +{ + return TREE_NO_FLAGS; +} + + +/** + * Deletes the global history tree. + */ +void history_global_cleanup(void) +{ +} + + +/** + * Adds a url to the global history. + * + * \param url the url to be added + */ +void global_history_add(const char *url) +{ + const struct url_data *data; + + data = urldb_get_url_data(url); + if (data == NULL) + return; + + global_history_add_internal(url, data); +} + + +/* Actions to be connected to front end specific toolbars */ + +/** + * Save the global history in a human-readable form under the given location. + * + * \param path the path where the history will be saved + */ +bool history_global_export(const char *path) +{ + return tree_urlfile_save(global_history_tree, path, "NetSurf history"); +} + +/** + * Delete nodes which are currently selected. + */ +void history_global_delete_selected(void) +{ + tree_delete_selected_nodes(global_history_tree, + global_history_tree_root); +} + +/** + * Delete all nodes. + */ +void history_global_delete_all(void) +{ + bool redraw_needed = tree_get_redraw(global_history_tree); + if (redraw_needed) + tree_set_redraw(global_history_tree, false); + + tree_set_node_selected(global_history_tree, global_history_tree_root, + true, true); + tree_delete_selected_nodes(global_history_tree, + global_history_tree_root); + + if (redraw_needed) + tree_set_redraw(global_history_tree, true); +} + +/** + * Select all nodes in the tree. + */ +void history_global_select_all(void) +{ + tree_set_node_selected(global_history_tree, global_history_tree_root, + true, true); +} + +/** + * Unselect all nodes. + */ +void history_global_clear_selection(void) +{ + tree_set_node_selected(global_history_tree, global_history_tree_root, + true, false); +} + +/** + * Expand grouping folders and history entries. + */ +void history_global_expand_all(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + true, true, true); +} + +/** + * Expand grouping folders only. + */ +void history_global_expand_directories(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + true, true, false); +} + +/** + * Expand history entries only. + */ +void history_global_expand_addresses(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + true, false, true); +} + +/** + * Collapse grouping folders and history entries. + */ +void history_global_collapse_all(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + false, true, true); +} + +/** + * Collapse grouping folders only. + */ +void history_global_collapse_directories(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + false, true, false); +} + +/** + * Collapse history entries only. + */ +void history_global_collapse_addresses(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + false, false, true); +} + +/** + * Open the selected entries in seperate browser windows. + */ +void history_global_launch_selected(void) +{ + tree_launch_selected(global_history_tree); +} diff --git a/desktop/history_global_core.h b/desktop/history_global_core.h new file mode 100644 index 000000000..97c578f3d --- /dev/null +++ b/desktop/history_global_core.h @@ -0,0 +1,44 @@ +/* + * Copyright 2005 Richard Wilson + * Copyright 2009 Paul Blokus + * + * 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_HISTORY_GLOBAL_H_ +#define _NETSURF_DESKTOP_HISTORY_GLOBAL_H_ + +#include + +#include "desktop/tree.h" + +bool history_global_initialise(struct tree *tree); +unsigned int history_global_get_tree_flags(void); +void history_global_cleanup(void); + +bool history_global_export(const char *path); +void history_global_delete_selected(void); +void history_global_delete_all(void); +void history_global_select_all(void); +void history_global_clear_selection(void); +void history_global_expand_all(void); +void history_global_expand_directories(void); +void history_global_expand_addresses(void); +void history_global_collapse_all(void); +void history_global_collapse_directories(void); +void history_global_collapse_addresses(void); +void history_global_launch_selected(void); + +#endif diff --git a/desktop/hotlist.c b/desktop/hotlist.c new file mode 100644 index 000000000..09be05709 --- /dev/null +++ b/desktop/hotlist.c @@ -0,0 +1,457 @@ +/* + * Copyright 2004, 2005 Richard Wilson + * Copyright 2009 Paul Blokus + * + * 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 "content/content.h" +#include "content/hlcache.h" +#include "content/urldb.h" +#include "desktop/browser.h" +#include "desktop/hotlist.h" +#include "desktop/plotters.h" +#include "desktop/tree.h" +#include "desktop/tree_url_node.h" + +#include "utils/messages.h" +#include "utils/utils.h" +#include "utils/log.h" + +#define URL_CHUNK_LENGTH 512 + +static struct tree *hotlist_tree; +static struct node *hotlist_tree_root; + +static bool creating_node; +static hlcache_handle *folder_icon; + +static const struct { + const char *url; + const char *msg_key; +} hotlist_default_entries[] = { + { "http://www.netsurf-browser.org/", "HotlistHomepage" }, + { "http://www.netsurf-browser.org/downloads/riscos/testbuilds", + "HotlistTestBuild" }, + { "http://www.netsurf-browser.org/documentation", + "HotlistDocumentation" }, + { "http://sourceforge.net/tracker/?atid=464312&group_id=51719", + "HotlistBugTracker" }, + { "http://sourceforge.net/tracker/?atid=464315&group_id=51719", + "HotlistFeatureRequest" } +}; +#define HOTLIST_ENTRIES_COUNT (sizeof(hotlist_default_entries) / sizeof(hotlist_default_entries[0])) + +static node_callback_resp hotlist_node_callback(void *user_data, + struct node_msg_data *msg_data) +{ + struct node *node = msg_data->node; + const char *text; + char *norm_text; + bool is_folder = tree_node_is_folder(node); + + switch (msg_data->msg) { + case NODE_ELEMENT_EDIT_FINISHED: + if (creating_node && + (is_folder == false) && + (msg_data->flag == TREE_ELEMENT_TITLE)) { + tree_url_node_edit_url(hotlist_tree, node); + } else { + creating_node = false; + } + return NODE_CALLBACK_HANDLED; + + case NODE_ELEMENT_EDIT_FINISHING: + if (creating_node && (is_folder == false)) + return tree_url_node_callback(hotlist_tree, msg_data); + + if (is_folder == true) { + text = msg_data->data.text; + while (isspace(*text)) + text++; + norm_text = strdup(text); + if (norm_text == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return NODE_CALLBACK_REJECT; + } + /* don't allow zero length entry text, return false */ + if (norm_text[0] == '\0') { + warn_user("NoNameError", 0); + msg_data->data.text = NULL; + return NODE_CALLBACK_CONTINUE; + } + msg_data->data.text = norm_text; + } + break; + + case NODE_DELETE_ELEMENT_IMG: + return NODE_CALLBACK_HANDLED; + + default: + if (is_folder == false) + return tree_url_node_callback(hotlist_tree, msg_data); + } + + return NODE_CALLBACK_NOT_HANDLED; +} + + +bool hotlist_initialise(struct tree *tree, const char *hotlist_path) +{ + struct node *node; + const struct url_data *url_data; + char *name; + int hlst_loop; + + /* Either load or create a hotlist */ + + creating_node = false; + + folder_icon = tree_load_icon(tree_directory_icon_name); + + tree_url_node_init(); + + if (tree == NULL) + return false; + + hotlist_tree = tree; + hotlist_tree_root = tree_get_root(hotlist_tree); + + if (tree_urlfile_load(hotlist_path, + hotlist_tree, + hotlist_node_callback, + NULL)) { + return true; + } + + + /* failed to load hotlist file, use default list */ + name = strdup("NetSurf"); + if (name == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return false; + } + node = tree_create_folder_node(hotlist_tree, hotlist_tree_root, + name, true, false, false); + if (node == NULL) { + free(name); + return false; + } + + tree_set_node_user_callback(node, hotlist_node_callback, NULL); + tree_set_node_icon(hotlist_tree, node, folder_icon); + + for (hlst_loop = 0; hlst_loop != HOTLIST_ENTRIES_COUNT; hlst_loop++) { + url_data = urldb_get_url_data(hotlist_default_entries[hlst_loop].url); + if (url_data == NULL) { + urldb_add_url(hotlist_default_entries[hlst_loop].url); + urldb_set_url_persistence( + hotlist_default_entries[hlst_loop].url, + true); + url_data = urldb_get_url_data( + hotlist_default_entries[hlst_loop].url); + } + if (url_data != NULL) { + tree_create_URL_node(hotlist_tree, node, + hotlist_default_entries[hlst_loop].url, + messages_get(hotlist_default_entries[hlst_loop].msg_key), + hotlist_node_callback, NULL); + tree_update_URL_node(hotlist_tree, node, + hotlist_default_entries[hlst_loop].url, + url_data, false); + } + } + + + + return true; +} + + +/** + * Get flags with which the hotlist tree should be created; + * + * \return the flags + */ +unsigned int hotlist_get_tree_flags(void) +{ + return TREE_MOVABLE; +} + + +/** + * Deletes the global history tree and saves the hotlist. + * \param hotlist_path the path where the hotlist should be saved + */ +void hotlist_cleanup(const char *hotlist_path) +{ + hotlist_export(hotlist_path); +} + + +/** + * Informs the hotlist that some content has been visited. Internal procedure. + * + * \param content the content visited + * \param node the node to update siblings and children of + */ +static void hotlist_visited_internal(hlcache_handle *content, struct node *node) +{ + struct node *child; + const char *text; + const char *url; + + if (content == NULL || + content_get_url(content) == NULL || + hotlist_tree == NULL) + return; + + url = content_get_url(content); + + for (; node; node = tree_node_get_next(node)) { + if (!tree_node_is_folder(node)) { + text = tree_url_node_get_url(node); + if (strcmp(text, url) == 0) { + tree_update_URL_node(hotlist_tree, node, + url, NULL, false); + } + } + child = tree_node_get_child(node); + if (child != NULL) { + hotlist_visited_internal(content, child); + } + } +} + +/** + * Informs the hotlist that some content has been visited + * + * \param content the content visited + */ +void hotlist_visited(hlcache_handle *content) +{ + if (hotlist_tree != NULL) { + hotlist_visited_internal(content, tree_get_root(hotlist_tree)); + } +} + +/** + * Save the hotlist in a human-readable form under the given location. + * + * \param path the path where the hotlist will be saved + */ +bool hotlist_export(const char *path) +{ + return tree_urlfile_save(hotlist_tree, path, "NetSurf hotlist"); +} + +/** + * Edit the node which is currently selected. Works only if one node is + * selected. + */ +void hotlist_edit_selected(void) +{ + struct node *node; + struct node_element *element; + + node = tree_get_selected_node(hotlist_tree_root); + + if (node != NULL) { + creating_node = true; + element = tree_node_find_element(node, TREE_ELEMENT_TITLE, NULL); + tree_start_edit(hotlist_tree, element); + } +} + +/** + * Delete nodes which are currently selected. + */ +void hotlist_delete_selected(void) +{ + tree_delete_selected_nodes(hotlist_tree, hotlist_tree_root); +} + +/** + * Select all nodes in the tree. + */ +void hotlist_select_all(void) +{ + tree_set_node_selected(hotlist_tree, hotlist_tree_root, + true, true); +} + +/** + * Unselect all nodes. + */ +void hotlist_clear_selection(void) +{ + tree_set_node_selected(hotlist_tree, hotlist_tree_root, + true, false); +} + +/** + * Expand grouping folders and history entries. + */ +void hotlist_expand_all(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + true, true, true); +} + +/** + * Expand grouping folders only. + */ +void hotlist_expand_directories(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + true, true, false); +} + +/** + * Expand history entries only. + */ +void hotlist_expand_addresses(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + true, false, true); +} + +/** + * Collapse grouping folders and history entries. + */ +void hotlist_collapse_all(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + false, true, true); +} + +/** + * Collapse grouping folders only. + */ +void hotlist_collapse_directories(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + false, true, false); +} + +/** + * Collapse history entries only. + */ +void hotlist_collapse_addresses(void) +{ + tree_set_node_expanded(hotlist_tree, + hotlist_tree_root, false, false, true); +} + +/** + * Add a folder node. + */ +void hotlist_add_folder(void) +{ + struct node *node; + struct node_element *element; + char *title = strdup("Untitled"); + + if (title == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return; + } + creating_node = true; + node = tree_create_folder_node(hotlist_tree, hotlist_tree_root, title, + true, false, false); + if (node == NULL) { + free(title); + return; + } + tree_set_node_user_callback(node, hotlist_node_callback, NULL); + tree_set_node_icon(hotlist_tree, node, folder_icon); + element = tree_node_find_element(node, TREE_ELEMENT_TITLE, NULL); + tree_start_edit(hotlist_tree, element); +} + +/** + * Add an entry node. + */ +void hotlist_add_entry(void) +{ + struct node *node; + creating_node = true; + node = tree_create_URL_node(hotlist_tree, hotlist_tree_root, "Address", + "Untitled", hotlist_node_callback, NULL); + + if (node == NULL) + return; + tree_set_node_user_callback(node, hotlist_node_callback, NULL); + tree_url_node_edit_title(hotlist_tree, node); +} + +/** + * Adds the currently viewed page to the hotlist + */ +void hotlist_add_page(const char *url) +{ + const struct url_data *data; + struct node *node; + + if (url == NULL) + return; + data = urldb_get_url_data(url); + if (data == NULL) + return; + + node = tree_create_URL_node(hotlist_tree, hotlist_tree_root, url, NULL, + hotlist_node_callback, NULL); + tree_update_URL_node(hotlist_tree, node, url, data, false); +} + +/** + * Adds the currently viewed page to the hotlist at the given cooridinates + * \param url url of the page + * \param x X cooridinate with respect to tree origin + * \param y Y cooridinate with respect to tree origin + */ +void hotlist_add_page_xy(const char *url, int x, int y) +{ + const struct url_data *data; + struct node *link, *node; + bool before; + + data = urldb_get_url_data(url); + if (data == NULL) { + urldb_add_url(url); + urldb_set_url_persistence(url, true); + data = urldb_get_url_data(url); + } + if (data != NULL) { + link = tree_get_link_details(hotlist_tree, x, y, &before); + node = tree_create_URL_node(NULL, NULL, url, + NULL, hotlist_node_callback, NULL); + tree_link_node(hotlist_tree, link, node, before); + } +} + +/** + * Open the selected entries in separate browser windows. + */ +void hotlist_launch_selected(void) +{ + tree_launch_selected(hotlist_tree); +} diff --git a/desktop/hotlist.h b/desktop/hotlist.h new file mode 100644 index 000000000..84f573a90 --- /dev/null +++ b/desktop/hotlist.h @@ -0,0 +1,54 @@ +/* + * Copyright 2004, 2005 Richard Wilson + * Copyright 2009 Paul Blokus + * + * 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 . + */ + + + +/** \file + * Hotlist (interface). + */ + +#ifndef _NETSURF_DESKTOP_HOTLIST_H_ +#define _NETSURF_DESKTOP_HOTLIST_H_ + +#include + +#include "desktop/tree.h" + +bool hotlist_initialise(struct tree *tree, const char *hotlist_path); +unsigned int hotlist_get_tree_flags(void); +void hotlist_cleanup(const char *hotlist_path); + +bool hotlist_export(const char *path); +void hotlist_edit_selected(void); +void hotlist_delete_selected(void); +void hotlist_select_all(void); +void hotlist_clear_selection(void); +void hotlist_expand_all(void); +void hotlist_expand_directories(void); +void hotlist_expand_addresses(void); +void hotlist_collapse_all(void); +void hotlist_collapse_directories(void); +void hotlist_collapse_addresses(void); +void hotlist_add_folder(void); +void hotlist_add_entry(void); +void hotlist_add_page(const char *url); +void hotlist_add_page_xy(const char *url, int x, int y); +void hotlist_launch_selected(void); + +#endif diff --git a/desktop/options.c b/desktop/options.c index 9ed8b192c..47b42dc3b 100644 --- a/desktop/options.c +++ b/desktop/options.c @@ -31,16 +31,10 @@ #include #include #include -#include -#include -#include "content/urldb.h" #include "css/css.h" #include "desktop/options.h" #include "desktop/plot_style.h" -#include "desktop/tree.h" #include "utils/log.h" -#include "utils/messages.h" -#include "utils/url.h" #include "utils/utils.h" #if defined(riscos) @@ -145,6 +139,7 @@ unsigned int option_min_reflow_period = 100; /* time in cs */ #else unsigned int option_min_reflow_period = 25; /* time in cs */ #endif +char *option_tree_icons_dir = NULL; bool option_core_select_menu = false; /** top margin of exported page*/ int option_margin_top = DEFAULT_MARGIN_TOP_MM; @@ -247,6 +242,7 @@ struct { { "scale", OPTION_INTEGER, &option_scale }, { "incremental_reflow", OPTION_BOOL, &option_incremental_reflow }, { "min_reflow_period", OPTION_INTEGER, &option_min_reflow_period }, + { "tree_icons_dir", OPTION_STRING, &option_tree_icons_dir }, { "core_select_menu", OPTION_BOOL, &option_core_select_menu }, /* Fetcher options */ { "max_fetchers", OPTION_INTEGER, &option_max_fetchers }, @@ -276,13 +272,6 @@ struct { #define option_table_entries (sizeof option_table / sizeof option_table[0]) -static void options_load_tree_directory(xmlNode *ul, struct node *directory); -static void options_load_tree_entry(xmlNode *li, struct node *directory); -xmlNode *options_find_tree_element(xmlNode *node, const char *name); -bool options_save_tree_directory(struct node *directory, xmlNode *node); -bool options_save_tree_entry(struct node *entry, xmlNode *node); - - /** * Read options from a file. * @@ -429,351 +418,3 @@ void options_dump(void) fprintf(stderr, "\n"); } } - -/** - * Loads a hotlist as a tree from a specified file. - * - * \param filename name of file to read - * \return the hotlist file represented as a tree, or NULL on failure - */ -struct tree *options_load_tree(const char *filename) { - xmlDoc *doc; - xmlNode *html, *body, *ul; - struct tree *tree; - - doc = htmlParseFile(filename, "iso-8859-1"); - if (!doc) { - warn_user("HotlistLoadError", messages_get("ParsingFail")); - return NULL; - } - - html = options_find_tree_element((xmlNode *) doc, "html"); - body = options_find_tree_element(html, "body"); - ul = options_find_tree_element(body, "ul"); - if (!ul) { - xmlFreeDoc(doc); - warn_user("HotlistLoadError", - "(......