/* * Copyright 2005 Richard Wilson * * 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 * Global history (implementation). */ #include #include #include #include #include #include #include "oslib/wimp.h" #include "oslib/wimpspriteop.h" #include "content/urldb.h" #include "desktop/tree.h" #include "riscos/dialog.h" #include "riscos/global_history.h" #include "riscos/gui.h" #include "riscos/menus.h" #include "riscos/options.h" #include "riscos/theme.h" #include "riscos/treeview.h" #include "riscos/wimp.h" #include "riscos/wimp_event.h" #include "utils/messages.h" #include "utils/log.h" #include "utils/url.h" #include "utils/utils.h" #define MAXIMUM_URL_LENGTH 1024 #define MAXIMUM_BASE_NODES 16 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 char *global_history_recent_url[GLOBAL_HISTORY_RECENT_URLS]; static int global_history_recent_count = 0; static bool global_history_init; static bool ro_gui_global_history_click(wimp_pointer *pointer); static void ro_gui_global_history_initialise_nodes(void); static void ro_gui_global_history_initialise_node(const char *title, time_t base, int days_back); static struct node *ro_gui_global_history_find(const char *url); static bool global_history_add_internal(const char *url, const struct url_data *data); /* The history window, toolbar and plot origins */ static wimp_w global_history_window; struct tree *global_history_tree; /** * Initialise global history tree */ void ro_gui_global_history_initialise(void) { char s[MAXIMUM_URL_LENGTH]; FILE *fp; /* create our window */ global_history_window = ro_gui_dialog_create("tree"); ro_gui_set_window_title(global_history_window, messages_get("GlobalHistory")); ro_gui_wimp_event_register_redraw_window(global_history_window, ro_gui_tree_redraw); ro_gui_wimp_event_register_open_window(global_history_window, ro_gui_tree_open); ro_gui_wimp_event_register_mouse_click(global_history_window, ro_gui_global_history_click); /* Create an empty tree */ global_history_tree = calloc(sizeof(struct tree), 1); if (!global_history_tree) { warn_user("NoMemory", 0); return; } global_history_tree->root = tree_create_folder_node(NULL, "Root"); if (!global_history_tree->root) { warn_user("NoMemory", 0); free(global_history_tree); global_history_tree = NULL; return; } global_history_tree->root->expanded = true; ro_gui_global_history_initialise_nodes(); global_history_tree->handle = (int)global_history_window; global_history_tree->movable = false; ro_gui_wimp_event_set_user_data(global_history_window, global_history_tree); ro_gui_wimp_event_register_keypress(global_history_window, ro_gui_tree_keypress); /* Create our toolbar */ global_history_tree->toolbar = ro_gui_theme_create_toolbar(NULL, THEME_HISTORY_TOOLBAR); if (global_history_tree->toolbar) ro_gui_theme_attach_toolbar(global_history_tree->toolbar, global_history_window); /* load recent URLs */ fp = fopen(option_recent_path, "r"); if (!fp) LOG(("Failed to open file '%s' for reading", option_recent_path)); else { while (fgets(s, MAXIMUM_URL_LENGTH, fp)) { if (s[strlen(s) - 1] == '\n') s[strlen(s) - 1] = '\0'; global_history_add_recent(s); } fclose(fp); } global_history_init = true; urldb_iterate_entries(global_history_add_internal); global_history_init = false; tree_initialise(global_history_tree); } /** * Initialises the base nodes */ void ro_gui_global_history_initialise_nodes(void) { struct tm *full_time; time_t t; int weekday; int i; /* get the current time */ t = time(NULL); if (t == -1) return; /* 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) return; ro_gui_global_history_initialise_node(messages_get("DateToday"), t, 0); if (weekday > 0) ro_gui_global_history_initialise_node( messages_get("DateYesterday"), t, -1); for (i = 2; i <= weekday; i++) ro_gui_global_history_initialise_node(NULL, t, -i); ro_gui_global_history_initialise_node(messages_get("Date1Week"), t, -weekday - 7); ro_gui_global_history_initialise_node(messages_get("Date2Week"), t, -weekday - 14); ro_gui_global_history_initialise_node(messages_get("Date3Week"), t, -weekday - 21); } /** * Create and initialise a node */ void ro_gui_global_history_initialise_node(const char *title, time_t base, int days_back) { struct tm *full_time; char buffer[64]; struct node *node; base += days_back * 60 * 60 * 24; if (!title) { full_time = localtime(&base); strftime((char *)&buffer, (size_t)64, "%A", full_time); node = tree_create_folder_node(NULL, buffer); } else node = tree_create_folder_node(NULL, title); if (!node) return; node->retain_in_memory = true; node->deleted = true; node->editable = false; 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++; } /** * Saves the global history's recent URL data. */ void ro_gui_global_history_save(void) { FILE *fp; int i; /* save recent URLs */ fp = fopen(option_recent_save, "w"); if (!fp) LOG(("Failed to open file '%s' for writing", option_recent_save)); else { for (i = global_history_recent_count - 1; i >= 0; i--) if (strlen(global_history_recent_url[i]) < MAXIMUM_URL_LENGTH) fprintf(fp, "%s\n", global_history_recent_url[i]); fclose(fp); } } /** * Respond to a mouse click * * \param pointer the pointer state * \return true to indicate click handled */ bool ro_gui_global_history_click(wimp_pointer *pointer) { ro_gui_tree_click(pointer, global_history_tree); if (pointer->buttons == wimp_CLICK_MENU) ro_gui_menu_create(global_history_menu, pointer->pos.x, pointer->pos.y, pointer->w); else ro_gui_menu_prepare_action(pointer->w, TREE_SELECTION, false); return true; } /** * Adds to the global history * * \param url The URL to add */ void global_history_add(const char *url) { const struct url_data *data; data = urldb_get_url_data(url); if (!data) return; global_history_add_internal(url, data); } /** * 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) */ 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 && data); 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) return true; if (parent->deleted) { /* 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 (!global_history_base_node[j]->deleted && 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, false); tree_set_node_expanded(global_history_tree, parent, false); tree_link_node(link, parent, before); if (!global_history_init) { tree_recalculate_node(global_history_tree, parent, true); tree_recalculate_node_positions(global_history_tree, global_history_tree->root); tree_redraw_area(global_history_tree, 0, 0, 16384, 16384); } } /* find any previous occurance */ if (!global_history_init) { node = ro_gui_global_history_find(url); if (node) { /* \todo: calculate old/new positions and redraw * only the relevant portion */ tree_redraw_area(global_history_tree, 0, 0, 16384, 16384); tree_update_URL_node(node, url, data); tree_delink_node(node); tree_link_node(parent, node, false); tree_handle_node_changed(global_history_tree, node, false, true); /* ro_gui_tree_scroll_visible(hotlist_tree, &node->data); */ return true; } } /* Add the node at the bottom */ node = tree_create_URL_node_shared(parent, url, data); if ((!global_history_init) && (node)) { tree_redraw_area(global_history_tree, node->box.x - NODE_INSTEP, 0, NODE_INSTEP, 16384); tree_handle_node_changed(global_history_tree, node, true, false); } return true; } /** * Find an entry in the global history * * \param url The URL to find * \return Pointer to node, or NULL if not found */ struct node *ro_gui_global_history_find(const char *url) { int i; struct node *node; struct node_element *element; for (i = 0; i < global_history_base_node_count; i++) { if (!global_history_base_node[i]->deleted) { for (node = global_history_base_node[i]->child; node; node = node->next) { element = tree_find_element(node, TREE_ELEMENT_URL); if ((element) && !strcmp(url, element->text)) return node; } } } return NULL; } /** * Adds an URL to the recently used list * * \param url the URL to add (copied) */ void global_history_add_recent(const char *url) { int i; int j = -1; char *current; /* try to find a string already there */ for (i = 0; i < global_history_recent_count; i++) if (global_history_recent_url[i] && !strcmp(global_history_recent_url[i], url)) j = i; /* already at head of list */ if (j == 0) return; if (j < 0) { /* add to head of list */ free(global_history_recent_url[ GLOBAL_HISTORY_RECENT_URLS - 1]); memmove(&global_history_recent_url[1], &global_history_recent_url[0], (GLOBAL_HISTORY_RECENT_URLS - 1) * sizeof(char *)); global_history_recent_url[0] = strdup(url); global_history_recent_count++; if (global_history_recent_count > GLOBAL_HISTORY_RECENT_URLS) global_history_recent_count = GLOBAL_HISTORY_RECENT_URLS; if (global_history_recent_count == 1) ro_gui_window_prepare_navigate_all(); } else { /* move to head of list */ current = global_history_recent_url[j]; for (i = j; i > 0; i--) global_history_recent_url[i] = global_history_recent_url[i - 1]; global_history_recent_url[0] = current; } } /** * Gets details of the currently used URL list. * * \param count set to the current number of entries in the URL array on exit * \return the current URL array */ char **global_history_get_recent(int *count) { *count = global_history_recent_count; return global_history_recent_url; }