netsurf/desktop/history_global_core.c

451 lines
11 KiB
C

/*
* Copyright 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 <stdlib.h>
#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"
#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 history_global_add_internal(nsurl *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(nsurl_access(url));
if (node != NULL) {
tree_update_URL_node(global_history_tree,
node, url, data);
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_readonly(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;
struct node *node;
base += days_back * 60 * 60 * 24;
if (title == NULL) {
full_time = localtime(&base);
title = messages_get(weekday_msg_name[full_time->tm_wday]);
}
node = tree_create_folder_node(NULL, NULL, title, false, true, true);
if (node == NULL) {
warn_user(messages_get_errorcode(NSERROR_NOMEM), 0);
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, const char* folder_icon_name)
{
folder_icon = tree_load_icon(folder_icon_name);
tree_url_node_init(folder_icon_name);
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;
LOG(("Building history tree"));
global_history_initialised = true;
urldb_iterate_entries(history_global_add_internal);
global_history_initialised = false;
tree_set_node_expanded(global_history_tree, global_history_tree_root,
false, true, true);
LOG(("History tree built"));
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 | TREE_HISTORY;
}
/**
* Deletes the global history tree.
*/
void history_global_cleanup(void)
{
hlcache_handle_release(folder_icon);
tree_url_node_cleanup();
}
/**
* Adds a url to the global history.
*
* \param url the url to be added
*/
void history_global_add(nsurl *url)
{
const struct url_data *data;
data = urldb_get_url_data(url);
if (data == NULL)
return;
history_global_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.
*
* \param tabs open multiple entries in tabs in the new window
*/
void history_global_launch_selected(bool tabs)
{
tree_launch_selected(global_history_tree, tabs);
}