451 lines
11 KiB
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;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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);
|
|
}
|