510 lines
12 KiB
C
510 lines
12 KiB
C
/*
|
|
* Copyright 2004, 2005 Richard Wilson <info@tinct.net>
|
|
* Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
|
|
#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);
|
|
bool cancelled = false;
|
|
|
|
switch (msg_data->msg) {
|
|
case NODE_ELEMENT_EDIT_CANCELLED:
|
|
cancelled = true;
|
|
/* fall through */
|
|
case NODE_ELEMENT_EDIT_FINISHED:
|
|
if (creating_node && !cancelled &&
|
|
(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;
|
|
}
|
|
|
|
/* exported interface documented in hotlist.h */
|
|
bool hotlist_initialise(struct tree *tree, const char *hotlist_path, const char* folder_icon_name)
|
|
{
|
|
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(folder_icon_name);
|
|
|
|
tree_url_node_init(folder_icon_name);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
hlcache_handle_release(folder_icon);
|
|
tree_url_node_cleanup();
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 ||
|
|
hlcache_handle_get_url(content) == NULL ||
|
|
hotlist_tree == NULL)
|
|
return;
|
|
|
|
/* TODO: do this with a nsurl instead */
|
|
url = nsurl_access(hlcache_handle_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);
|
|
}
|
|
}
|
|
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.
|
|
*
|
|
* \param selected create the folder in the currently-selected node
|
|
*/
|
|
void hotlist_add_folder(bool selected)
|
|
{
|
|
struct node *node, *parent = NULL;
|
|
struct node_element *element;
|
|
char *title = strdup("Untitled");
|
|
|
|
if (title == NULL) {
|
|
LOG(("malloc failed"));
|
|
warn_user("NoMemory", 0);
|
|
return;
|
|
}
|
|
creating_node = true;
|
|
|
|
if (selected == true) {
|
|
parent = tree_get_selected_node(tree_get_root(hotlist_tree));
|
|
if (parent && (tree_node_is_folder(parent) == false)) {
|
|
parent = tree_node_get_parent(parent);
|
|
}
|
|
}
|
|
|
|
if (parent == NULL) {
|
|
parent = tree_get_default_folder_node(hotlist_tree);
|
|
}
|
|
|
|
node = tree_create_folder_node(hotlist_tree, parent, 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.
|
|
*
|
|
* \param selected add the entry in the currently-selected node
|
|
*/
|
|
void hotlist_add_entry(bool selected)
|
|
{
|
|
struct node *node;
|
|
struct node *parent = NULL;
|
|
creating_node = true;
|
|
|
|
if (selected == true) {
|
|
parent = tree_get_selected_node(tree_get_root(hotlist_tree));
|
|
if (parent && (tree_node_is_folder(parent) == false)) {
|
|
parent = tree_node_get_parent(parent);
|
|
}
|
|
}
|
|
|
|
if (parent == NULL) {
|
|
parent = tree_get_default_folder_node(hotlist_tree);
|
|
}
|
|
|
|
node = tree_create_URL_node(hotlist_tree, parent, "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, *parent;
|
|
|
|
if (url == NULL)
|
|
return;
|
|
data = urldb_get_url_data(url);
|
|
if (data == NULL)
|
|
return;
|
|
|
|
parent = tree_get_default_folder_node(hotlist_tree);
|
|
node = tree_create_URL_node(hotlist_tree, parent, url, NULL,
|
|
hotlist_node_callback, NULL);
|
|
tree_update_URL_node(hotlist_tree, node, url, data);
|
|
}
|
|
|
|
/**
|
|
* Adds the currently viewed page to the hotlist at the given co-ordinates
|
|
* \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.
|
|
*
|
|
* \param tabs open multiple entries in tabs in the new window
|
|
*/
|
|
void hotlist_launch_selected(bool tabs)
|
|
{
|
|
tree_launch_selected(hotlist_tree, tabs);
|
|
}
|
|
|
|
/**
|
|
* Set the hotlist's default folder to the selected node.
|
|
*
|
|
* \param clear reset the default to tree root
|
|
*/
|
|
bool hotlist_set_default_folder(bool clear)
|
|
{
|
|
if (clear == true) {
|
|
tree_clear_default_folder_node(hotlist_tree);
|
|
return true;
|
|
} else {
|
|
return tree_set_default_folder_node(hotlist_tree, NULL);
|
|
}
|
|
}
|