Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2005 Richard Wilson <info@tinct.net>
  3.  * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
  4.  *
  5.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  6.  *
  7.  * NetSurf is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; version 2 of the License.
  10.  *
  11.  * NetSurf is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  */
  19.  
  20.  
  21. #include <stdlib.h>
  22.  
  23. #include "content/content.h"
  24. #include "content/hlcache.h"
  25. #include "content/urldb.h"
  26. #include "desktop/browser.h"
  27. #include "desktop/history_global_core.h"
  28. #include "desktop/plotters.h"
  29. #include "desktop/tree.h"
  30. #include "desktop/tree_url_node.h"
  31. #include "utils/messages.h"
  32. #include "utils/utils.h"
  33. #include "utils/log.h"
  34.  
  35. #define MAXIMUM_BASE_NODES 16
  36. #define GLOBAL_HISTORY_RECENT_URLS 16
  37. #define URL_CHUNK_LENGTH 512
  38.  
  39. static struct node *global_history_base_node[MAXIMUM_BASE_NODES];
  40. static int global_history_base_node_time[MAXIMUM_BASE_NODES];
  41. static int global_history_base_node_count = 0;
  42.  
  43. static bool global_history_initialised;
  44.  
  45. static struct tree *global_history_tree;
  46. static struct node *global_history_tree_root;
  47.  
  48. static hlcache_handle *folder_icon;
  49.  
  50. static const char *const weekday_msg_name [] =
  51. {
  52.         "Sunday",
  53.         "Monday",
  54.         "Tuesday",
  55.         "Wednesday",
  56.         "Thursday",
  57.         "Friday",
  58.         "Saturday"
  59. };
  60.  
  61. /**
  62.  * Find an entry in the global history
  63.  *
  64.  * \param url The URL to find
  65.  * \return Pointer to node, or NULL if not found
  66.  */
  67. static struct node *history_global_find(const char *url)
  68. {
  69.         int i;
  70.         struct node *node;
  71.         const char *text;
  72.  
  73.         for (i = 0; i < global_history_base_node_count; i++) {
  74.                 if (!tree_node_is_deleted(global_history_base_node[i])) {
  75.                         node = tree_node_get_child(global_history_base_node[i]);
  76.                         for (; node != NULL; node = tree_node_get_next(node)) {
  77.                                 text = tree_url_node_get_url(node);
  78.                                 if ((text != NULL) && !strcmp(url, text))
  79.                                         return node;
  80.                         }
  81.                 }
  82.         }
  83.         return NULL;
  84. }
  85.  
  86. /**
  87.  * Internal routine to actually perform global history addition
  88.  *
  89.  * \param url The URL to add
  90.  * \param data URL data associated with URL
  91.  * \return true (for urldb_iterate_entries)
  92.  */
  93. static bool global_history_add_internal(nsurl *url, const struct url_data *data)
  94. {
  95.         int i, j;
  96.         struct node *parent = NULL;
  97.         struct node *link;
  98.         struct node *node;
  99.         bool before = false;
  100.         int visit_date;
  101.  
  102.         assert((url != NULL) && (data != NULL));
  103.  
  104.         visit_date = data->last_visit;
  105.  
  106.         /* find parent node */
  107.         for (i = 0; i < global_history_base_node_count; i++) {
  108.                 if (global_history_base_node_time[i] <= visit_date) {
  109.                         parent = global_history_base_node[i];
  110.                         break;
  111.                 }
  112.         }
  113.  
  114.         /* the entry is too old to care about */
  115.         if (parent == NULL)
  116.                 return true;
  117.  
  118.         if (tree_node_is_deleted(parent)) {
  119.                 /* parent was deleted, so find place to insert it */
  120.                 link = global_history_tree_root;
  121.  
  122.                 for (j = global_history_base_node_count - 1; j >= 0; j--) {
  123.                         if (!tree_node_is_deleted(global_history_base_node[j]) &&
  124.                             global_history_base_node_time[j] >
  125.                             global_history_base_node_time[i]) {
  126.                                 link = global_history_base_node[j];
  127.                                 before = true;
  128.                                 break;
  129.                         }
  130.                 }
  131.  
  132.                 tree_set_node_selected(global_history_tree,
  133.                                        parent, true, false);
  134.                 tree_set_node_expanded(global_history_tree,
  135.                                        parent, false, true, true);
  136.                 tree_link_node(global_history_tree, link, parent, before);
  137.         }
  138.  
  139.         /* find any previous occurance */
  140.         if (global_history_initialised == false) {
  141.                 node = history_global_find(nsurl_access(url));
  142.                 if (node != NULL) {
  143.                         tree_update_URL_node(global_history_tree,
  144.                                              node, url, data);
  145.                         tree_delink_node(global_history_tree, node);
  146.                         tree_link_node(global_history_tree, parent, node,
  147.                                        false);
  148.                         return true;
  149.                 }
  150.         }
  151.  
  152.         /* Add the node at the bottom */
  153.         node = tree_create_URL_node_readonly(global_history_tree,
  154.                                            parent, url, data,
  155.                                            tree_url_node_callback, NULL);
  156.  
  157.         return true;
  158. }
  159.  
  160. static node_callback_resp
  161. history_global_node_callback(void *user_data,
  162.                              struct node_msg_data *msg_data)
  163. {
  164.         if (msg_data->msg == NODE_DELETE_ELEMENT_IMG)
  165.                 return NODE_CALLBACK_HANDLED;
  166.         return NODE_CALLBACK_NOT_HANDLED;
  167. }
  168.  
  169. /**
  170.  * Initialises a single grouping node for the global history tree.
  171.  *
  172.  * \return false on memory exhaustion, true otherwise
  173.  */
  174. static bool history_global_initialise_node(const char *title,
  175.                                            time_t base, int days_back)
  176. {
  177.         struct tm *full_time;
  178.         char *buffer;
  179.         struct node *node;
  180.  
  181.         base += days_back * 60 * 60 * 24;
  182.         if (title == NULL) {
  183.                 full_time = localtime(&base);
  184.                 buffer = strdup(messages_get(weekday_msg_name[full_time->tm_wday]));
  185.         } else {
  186.                 buffer = strdup(title);
  187.         }
  188.  
  189.         if (buffer == NULL) {
  190.                 LOG(("malloc failed"));
  191.                 warn_user("NoMemory", 0);
  192.                 return false;
  193.         }
  194.  
  195.         node = tree_create_folder_node(NULL, NULL, buffer,
  196.                                        false, true, true);
  197.         if (node == NULL) {
  198.                 LOG(("malloc failed"));
  199.                 warn_user("NoMemory", 0);
  200.                 free(buffer);
  201.                 return false;
  202.         }
  203.         if (folder_icon != NULL)
  204.                 tree_set_node_icon(global_history_tree, node, folder_icon);
  205.         tree_set_node_user_callback(node, history_global_node_callback, NULL);
  206.  
  207.         global_history_base_node[global_history_base_node_count] = node;
  208.         global_history_base_node_time[global_history_base_node_count] = base;
  209.         global_history_base_node_count++;
  210.  
  211.         return true;
  212. }
  213.  
  214. /**
  215.  * Initialises the grouping nodes(Today, Yesterday etc.) for the global history
  216.  * tree.
  217.  *
  218.  * \return false on memory exhaustion, true otherwise
  219.  */
  220. static bool history_global_initialise_nodes(void)
  221. {
  222.         struct tm *full_time;
  223.         time_t t;
  224.         int weekday;
  225.         int i;
  226.  
  227.         /* get the current time */
  228.         t = time(NULL);
  229.         if (t == -1) {
  230.                 LOG(("time info unaviable"));
  231.                 return false;
  232.         }
  233.  
  234.         /* get the time at the start of today */
  235.         full_time = localtime(&t);
  236.         weekday = full_time->tm_wday;
  237.         full_time->tm_sec = 0;
  238.         full_time->tm_min = 0;
  239.         full_time->tm_hour = 0;
  240.         t = mktime(full_time);
  241.         if (t == -1) {
  242.                 LOG(("mktime failed"));
  243.                 return false;
  244.         }
  245.  
  246.         history_global_initialise_node(messages_get("DateToday"), t, 0);
  247.         if (weekday > 0)
  248.                 if (!history_global_initialise_node(
  249.                             messages_get("DateYesterday"), t, -1))
  250.                         return false;
  251.         for (i = 2; i <= weekday; i++)
  252.                 if (!history_global_initialise_node(NULL, t, -i))
  253.                         return false;
  254.  
  255.         if (!history_global_initialise_node(messages_get("Date1Week"),
  256.                                             t, -weekday - 7))
  257.                 return false;
  258.         if (!history_global_initialise_node(messages_get("Date2Week"),
  259.                                             t, -weekday - 14))
  260.                 return false;
  261.         if (!history_global_initialise_node(messages_get("Date3Week"),
  262.                                             t, -weekday - 21))
  263.                 return false;
  264.  
  265.         return true;
  266. }
  267.  
  268. /**
  269.  * Initialises the global history tree.
  270.  *
  271.  * \param data          user data for the callbacks
  272.  * \param start_redraw  callback function called before every redraw
  273.  * \param end_redraw    callback function called after every redraw
  274.  * \return true on success, false on memory exhaustion
  275.  */
  276. bool history_global_initialise(struct tree *tree, const char* folder_icon_name)
  277. {
  278.         folder_icon = tree_load_icon(folder_icon_name);
  279.         tree_url_node_init(folder_icon_name);
  280.  
  281.         if (tree == NULL)
  282.                 return false;
  283.  
  284.         global_history_tree = tree;
  285.         global_history_tree_root = tree_get_root(global_history_tree);
  286.  
  287.         if (!history_global_initialise_nodes())
  288.                 return false;
  289.  
  290.         LOG(("Building history tree"));
  291.  
  292.         global_history_initialised = true;
  293.         urldb_iterate_entries(global_history_add_internal);
  294.         global_history_initialised = false;
  295.         tree_set_node_expanded(global_history_tree, global_history_tree_root,
  296.                                false, true, true);
  297.         LOG(("History tree built"));
  298.         return true;
  299. }
  300.  
  301.  
  302. /**
  303.  * Get flags with which the global history tree should be created;
  304.  *
  305.  * \return the flags
  306.  */
  307. unsigned int history_global_get_tree_flags(void)
  308. {
  309.         return TREE_NO_FLAGS;
  310. }
  311.  
  312.  
  313. /**
  314.  * Deletes the global history tree.
  315.  */
  316. void history_global_cleanup(void)
  317. {
  318.         hlcache_handle_release(folder_icon);
  319.         tree_url_node_cleanup();
  320. }
  321.  
  322.  
  323. /**
  324.  * Adds a url to the global history.
  325.  *
  326.  * \param url the url to be added
  327.  */
  328. void global_history_add(nsurl *url)
  329. {
  330.         const struct url_data *data;
  331.  
  332.         data = urldb_get_url_data(url);
  333.         if (data == NULL)
  334.                 return;
  335.  
  336.         global_history_add_internal(url, data);
  337. }
  338.  
  339.  
  340. /* Actions to be connected to front end specific toolbars */
  341.  
  342. /**
  343.  * Save the global history in a human-readable form under the given location.
  344.  *
  345.  * \param path the path where the history will be saved
  346.  */
  347. bool history_global_export(const char *path)
  348. {
  349.         return tree_urlfile_save(global_history_tree, path, "NetSurf history");
  350. }
  351.  
  352. /**
  353.  * Delete nodes which are currently selected.
  354.  */
  355. void history_global_delete_selected(void)
  356. {
  357.         tree_delete_selected_nodes(global_history_tree,
  358.                                    global_history_tree_root);
  359. }
  360.  
  361. /**
  362.  * Delete all nodes.
  363.  */
  364. void history_global_delete_all(void)
  365. {
  366.         bool redraw_needed = tree_get_redraw(global_history_tree);
  367.         if (redraw_needed)
  368.                 tree_set_redraw(global_history_tree, false);
  369.  
  370.         tree_set_node_selected(global_history_tree, global_history_tree_root,
  371.                                true, true);
  372.         tree_delete_selected_nodes(global_history_tree,
  373.                                    global_history_tree_root);
  374.  
  375.         if (redraw_needed)
  376.                 tree_set_redraw(global_history_tree, true);
  377. }
  378.  
  379. /**
  380.  * Select all nodes in the tree.
  381.  */
  382. void history_global_select_all(void)
  383. {
  384.         tree_set_node_selected(global_history_tree, global_history_tree_root,
  385.                                true, true);
  386. }
  387.  
  388. /**
  389.  * Unselect all nodes.
  390.  */
  391. void history_global_clear_selection(void)
  392. {
  393.         tree_set_node_selected(global_history_tree, global_history_tree_root,
  394.                                true, false);
  395. }
  396.  
  397. /**
  398.  * Expand grouping folders and history entries.
  399.  */
  400. void history_global_expand_all(void)
  401. {
  402.         tree_set_node_expanded(global_history_tree, global_history_tree_root,
  403.                                true, true, true);
  404. }
  405.  
  406. /**
  407.  * Expand grouping folders only.
  408.  */
  409. void history_global_expand_directories(void)
  410. {
  411.         tree_set_node_expanded(global_history_tree, global_history_tree_root,
  412.                                true, true, false);
  413. }
  414.  
  415. /**
  416.  * Expand history entries only.
  417.  */
  418. void history_global_expand_addresses(void)
  419. {
  420.         tree_set_node_expanded(global_history_tree, global_history_tree_root,
  421.                                true, false, true);
  422. }
  423.  
  424. /**
  425.  * Collapse grouping folders and history entries.
  426.  */
  427. void history_global_collapse_all(void)
  428. {
  429.         tree_set_node_expanded(global_history_tree, global_history_tree_root,
  430.                                false, true, true);
  431. }
  432.  
  433. /**
  434.  * Collapse grouping folders only.
  435.  */
  436. void history_global_collapse_directories(void)
  437. {
  438.         tree_set_node_expanded(global_history_tree, global_history_tree_root,
  439.                                false, true, false);
  440. }
  441.  
  442. /**
  443.  * Collapse history entries only.
  444.  */
  445. void history_global_collapse_addresses(void)
  446. {
  447.         tree_set_node_expanded(global_history_tree, global_history_tree_root,
  448.                                false, false, true);
  449. }
  450.  
  451. /**
  452.  * Open the selected entries in seperate browser windows.
  453.  *
  454.  * \param  tabs  open multiple entries in tabs in the new window
  455.  */
  456. void history_global_launch_selected(bool tabs)
  457. {
  458.         tree_launch_selected(global_history_tree, tabs);
  459. }
  460.