From d04d1e442d86adc556038f360c251091e4529f68 Mon Sep 17 00:00:00 2001 From: Imran Patel Date: Tue, 24 Jul 2007 14:36:54 -0700 Subject: [PATCH] Initial stab at merging sqlite3 prototype with auto-completion (incomplete) --- configure.ac | 8 + embed/Makefile.am | 6 + embed/ephy-history.c | 1941 ++++++++++++++++++++----------------------- embed/ephy-history.h | 224 +++-- src/Makefile.am | 1 + src/ephy-completion-model.c | 172 ++-- 6 files changed, 1131 insertions(+), 1221 deletions(-) rewrite embed/ephy-history.c (96%) rewrite embed/ephy-history.h (94%) diff --git a/configure.ac b/configure.ac index 3341610..33a1571 100644 --- a/configure.ac +++ b/configure.ac @@ -105,6 +105,14 @@ PKG_CHECK_MODULES([DEPENDENCIES], [ AC_SUBST([DEPENDENCIES_CFLAGS]) AC_SUBST([DEPENDENCIES_LIBS]) +# ******* +# SQLite3 +# ******* + +PKG_CHECK_MODULES([SQLITE3],[sqlite3 >= 3.3]) +AC_SUBST([SQLITE3_CFLAGS]) +AC_SUBST([SQLITE3_LIBS]) + # **** # DBUS # **** diff --git a/embed/Makefile.am b/embed/Makefile.am index b0e648e..598ef98 100644 --- a/embed/Makefile.am +++ b/embed/Makefile.am @@ -28,6 +28,8 @@ INST_H_FILES = \ ephy-embed-single.h \ ephy-embed-shell.h \ ephy-history.h \ + ephy-history-node.h \ + ephy-history-page-node.h \ ephy-password-manager.h \ ephy-permission-manager.h @@ -52,6 +54,8 @@ libephyembed_la_SOURCES = \ ephy-encodings.c \ ephy-favicon-cache.c \ ephy-history.c \ + ephy-history-node.c \ + ephy-history-page-node.c \ ephy-password-manager.c \ ephy-permission-manager.c \ $(INST_H_FILES) \ @@ -82,6 +86,7 @@ libephyembed_la_CFLAGS = \ -I$(MOZILLA_INCLUDE_ROOT)/gtkembedmoz \ -DSHARE_DIR=\"$(pkgdatadir)\" \ $(DEPENDENCIES_CFLAGS) \ + $(SQLITE3_CFLAGS) \ $(AM_CFLAGS) libephyembedfactory_la_SOURCES = \ @@ -95,6 +100,7 @@ libephyembedfactory_la_CPPFLAGS = \ libephyembedfactory_la_CFLAGS = \ $(DEPENDENCIES_CFLAGS) \ + $(SQLITE3_CFLAGS) \ $(AM_CFLAGS) CLEANFILES = $(stamp_files) $(BUILT_SOURCES) diff --git a/embed/ephy-history.c b/embed/ephy-history.c dissimilarity index 96% index 99b834b..b588563 100644 --- a/embed/ephy-history.c +++ b/embed/ephy-history.c @@ -1,1028 +1,913 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright © 2002, 2003 Marco Pesenti Gritti - * - * This program 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; either version 2, or (at your option) - * any later version. - * - * This program 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * $Id: ephy-history.c 6952 2007-03-11 19:42:02Z chpe $ - */ - -#include "config.h" - -#include "ephy-history.h" -#include "ephy-marshal.h" -#include "ephy-file-helpers.h" -#include "ephy-debug.h" -#include "ephy-node-db.h" -#include "ephy-node-common.h" -#include "eel-gconf-extensions.h" -#include "ephy-prefs.h" -#include "ephy-glib-compat.h" - -#include -#include -#include -#include - -#define EPHY_HISTORY_XML_ROOT (const xmlChar *)"ephy_history" -#define EPHY_HISTORY_XML_VERSION (const xmlChar *)"1.0" - -/* how often to save the history, in seconds */ -#define HISTORY_SAVE_INTERVAL (5 * 60) - -/* if you change this remember to change also the user interface description */ -#define HISTORY_PAGE_OBSOLETE_DAYS 10 - -/* the number of seconds in a day */ -#define SECS_PER_DAY (60*60*24) - -#define EPHY_HISTORY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_HISTORY, EphyHistoryPrivate)) - -struct _EphyHistoryPrivate -{ - char *xml_file; - EphyNodeDb *db; - EphyNode *hosts; - EphyNode *pages; - EphyNode *last_page; - GHashTable *hosts_hash; - GHashTable *pages_hash; - guint autosave_timeout; - guint update_hosts_idle; - guint disable_history_notifier_id; - gboolean dirty; - gboolean enabled; -}; - -enum -{ - REDIRECT_FLAG = 1 << 0, - TOPLEVEL_FLAG = 1 << 1 -}; - -enum -{ - PROP_0, - PROP_ENABLED -}; - -enum -{ - ADD_PAGE, - VISITED, - CLEARED, - REDIRECT, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -static void ephy_history_class_init (EphyHistoryClass *klass); -static void ephy_history_init (EphyHistory *history); -static void ephy_history_finalize (GObject *object); -static gboolean impl_add_page (EphyHistory *, const char *, gboolean, gboolean); - -static GObjectClass *parent_class = NULL; - -GType -ephy_history_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) - { - const GTypeInfo our_info = - { - sizeof (EphyHistoryClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) ephy_history_class_init, - NULL, - NULL, /* class_data */ - sizeof (EphyHistory), - 0, /* n_preallocs */ - (GInstanceInitFunc) ephy_history_init - }; - - type = g_type_register_static (G_TYPE_OBJECT, - "EphyHistory", - &our_info, 0); - } - - return type; -} - -static void -ephy_history_set_enabled (EphyHistory *history, - gboolean enabled) -{ - LOG ("ephy_history_set_enabled %d", enabled); - - history->priv->enabled = enabled; - - ephy_node_db_set_immutable (history->priv->db, !enabled); - - if (enabled == FALSE) - { - ephy_history_clear (history); - } -} - -static void -ephy_history_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - EphyHistory *history = EPHY_HISTORY (object); - - switch (prop_id) - { - case PROP_ENABLED: - ephy_history_set_enabled (history, g_value_get_boolean (value)); - break; - } -} - -static void -ephy_history_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - EphyHistory *history = EPHY_HISTORY (object); - - switch (prop_id) - { - case PROP_ENABLED: - g_value_set_boolean (value, history->priv->enabled); - break; - } -} - -static void -ephy_history_class_init (EphyHistoryClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - object_class->finalize = ephy_history_finalize; - object_class->get_property = ephy_history_get_property; - object_class->set_property = ephy_history_set_property; - - klass->add_page = impl_add_page; - - g_object_class_install_property (object_class, - PROP_ENABLED, - g_param_spec_boolean ("enabled", - "Enabled", - "Enabled", - TRUE, - G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); - - signals[ADD_PAGE] = - g_signal_new ("add_page", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EphyHistoryClass, add_page), - g_signal_accumulator_true_handled, NULL, - ephy_marshal_BOOLEAN__STRING_BOOLEAN_BOOLEAN, - G_TYPE_BOOLEAN, - 3, - G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - - signals[VISITED] = - g_signal_new ("visited", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (EphyHistoryClass, visited), - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[CLEARED] = - g_signal_new ("cleared", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (EphyHistoryClass, cleared), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - signals[REDIRECT] = - g_signal_new ("redirect", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (EphyHistoryClass, redirect), - NULL, NULL, - ephy_marshal_VOID__STRING_STRING, - G_TYPE_NONE, - 2, - G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE, - G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE); - - g_type_class_add_private (object_class, sizeof (EphyHistoryPrivate)); -} - -static gboolean -page_is_obsolete (EphyNode *node, time_t now) -{ - int last_visit; - - last_visit = ephy_node_get_property_int - (node, EPHY_NODE_PAGE_PROP_LAST_VISIT); - return now - last_visit >= HISTORY_PAGE_OBSOLETE_DAYS*SECS_PER_DAY; -} - -static void -remove_obsolete_pages (EphyHistory *eb) -{ - GPtrArray *children; - int i; - time_t now; - - now = time (NULL); - - children = ephy_node_get_children (eb->priv->pages); - for (i = (int) children->len - 1; i >= 0; i--) - { - EphyNode *kid; - - kid = g_ptr_array_index (children, i); - - if (page_is_obsolete (kid, now)) - { - ephy_node_unref (kid); - } - } -} - -static gboolean -save_filter (EphyNode *node, - EphyNode *page_node) -{ - return node != page_node; -} - -static void -ephy_history_save (EphyHistory *eb) -{ - int ret; - - /* only save if there are changes */ - if (eb->priv->dirty == FALSE) - { - return; - } - - LOG ("Saving history db"); - - ret = ephy_node_db_write_to_xml_safe - (eb->priv->db, (const xmlChar *)eb->priv->xml_file, - EPHY_HISTORY_XML_ROOT, - EPHY_HISTORY_XML_VERSION, - NULL, /* comment */ - eb->priv->hosts, - (EphyNodeFilterFunc) save_filter, eb->priv->pages, - eb->priv->pages, NULL, NULL, - NULL); - - if (ret >=0) - { - /* save was successful */ - eb->priv->dirty = FALSE; - } -} - -static void -hosts_added_cb (EphyNode *node, - EphyNode *child, - EphyHistory *eb) -{ - eb->priv->dirty = TRUE; - - g_hash_table_insert (eb->priv->hosts_hash, - (char *) ephy_node_get_property_string (child, EPHY_NODE_PAGE_PROP_LOCATION), - child); -} - -static void -hosts_removed_cb (EphyNode *node, - EphyNode *child, - guint old_index, - EphyHistory *eb) -{ - eb->priv->dirty = TRUE; - - g_hash_table_remove (eb->priv->hosts_hash, - ephy_node_get_property_string (child, EPHY_NODE_PAGE_PROP_LOCATION)); -} - -static void -hosts_changed_cb (EphyNode *node, - EphyNode *child, - guint property_id, - EphyHistory *eb) -{ - eb->priv->dirty = TRUE; -} - -static void -pages_added_cb (EphyNode *node, - EphyNode *child, - EphyHistory *eb) -{ - eb->priv->dirty = TRUE; - - g_hash_table_insert (eb->priv->pages_hash, - (char *) ephy_node_get_property_string (child, EPHY_NODE_PAGE_PROP_LOCATION), - child); -} - -static void -pages_removed_cb (EphyNode *node, - EphyNode *child, - guint old_index, - EphyHistory *eb) -{ - eb->priv->dirty = TRUE; - - g_hash_table_remove (eb->priv->pages_hash, - ephy_node_get_property_string (child, EPHY_NODE_PAGE_PROP_LOCATION)); -} - -static void -pages_changed_cb (EphyNode *node, - EphyNode *child, - guint property_id, - EphyHistory *eb) -{ - eb->priv->dirty = TRUE; -} - -static gboolean -periodic_save_cb (EphyHistory *eh) -{ - remove_obsolete_pages (eh); - ephy_history_save (eh); - - return TRUE; -} - -static void -update_host_on_child_remove (EphyNode *node) -{ - GPtrArray *children; - int i, host_last_visit, new_host_last_visit = 0; - - host_last_visit = ephy_node_get_property_int - (node, EPHY_NODE_PAGE_PROP_LAST_VISIT); - - children = ephy_node_get_children (node); - for (i = 0; i < children->len; i++) - { - EphyNode *kid; - int last_visit; - - kid = g_ptr_array_index (children, i); - - last_visit = ephy_node_get_property_int - (kid, EPHY_NODE_PAGE_PROP_LAST_VISIT); - - if (last_visit > new_host_last_visit) - { - new_host_last_visit = last_visit; - } - } - - if (host_last_visit != new_host_last_visit) - { - ephy_node_set_property_int (node, - EPHY_NODE_PAGE_PROP_LAST_VISIT, - new_host_last_visit); - } -} - -static gboolean -update_hosts (EphyHistory *eh) -{ - GPtrArray *children; - int i; - GList *empty = NULL; - - children = ephy_node_get_children (eh->priv->hosts); - for (i = 0; i < children->len; i++) - { - EphyNode *kid; - - kid = g_ptr_array_index (children, i); - - if (kid != eh->priv->pages) - { - if (ephy_node_get_n_children (kid) > 0) - { - update_host_on_child_remove (kid); - } - else - { - empty = g_list_prepend (empty, kid); - } - } - } - - g_list_foreach (empty, (GFunc)ephy_node_unref, NULL); - g_list_free (empty); - - eh->priv->update_hosts_idle = 0; - - return FALSE; -} - -static void -page_removed_from_host_cb (EphyNode *node, - EphyNode *child, - guint old_index, - EphyHistory *eb) -{ - if (eb->priv->update_hosts_idle == 0) - { - eb->priv->update_hosts_idle = g_idle_add - ((GSourceFunc)update_hosts, eb); - } -} - -static void -remove_pages_from_host_cb (EphyNode *host, - EphyHistory *eh) -{ - GPtrArray *children; - EphyNode *site; - int i; - - children = ephy_node_get_children (host); - - for (i = (int) children->len - 1; i >= 0; i--) - { - site = g_ptr_array_index (children, i); - - ephy_node_unref (site); - } -} - -static void -connect_page_removed_from_host (char *url, - EphyNode *node, - EphyHistory *eb) -{ - if (node == eb->priv->pages) return; - - ephy_node_signal_connect_object (node, - EPHY_NODE_CHILD_REMOVED, - (EphyNodeCallback) page_removed_from_host_cb, - G_OBJECT (eb)); - ephy_node_signal_connect_object (node, - EPHY_NODE_DESTROY, - (EphyNodeCallback) remove_pages_from_host_cb, - G_OBJECT (eb)); -} - -static void -disable_history_notifier (GConfClient *client, - guint cnxn_id, - GConfEntry *entry, - EphyHistory *history) -{ - ephy_history_set_enabled - (history, !eel_gconf_get_boolean (CONF_LOCKDOWN_DISABLE_HISTORY)); -} - -static void -ephy_history_init (EphyHistory *eb) -{ - EphyNodeDb *db; - const char *all = _("All"); - - eb->priv = EPHY_HISTORY_GET_PRIVATE (eb); - eb->priv->update_hosts_idle = 0; - eb->priv->enabled = TRUE; - - db = ephy_node_db_new (EPHY_NODE_DB_HISTORY); - eb->priv->db = db; - - eb->priv->xml_file = g_build_filename (ephy_dot_dir (), - "ephy-history.xml", - NULL); - - eb->priv->pages_hash = g_hash_table_new (g_str_hash, - g_str_equal); - eb->priv->hosts_hash = g_hash_table_new (g_str_hash, - g_str_equal); - - /* Pages */ - eb->priv->pages = ephy_node_new_with_id (db, PAGES_NODE_ID); - - ephy_node_set_property_string (eb->priv->pages, - EPHY_NODE_PAGE_PROP_LOCATION, - all); - ephy_node_set_property_string (eb->priv->pages, - EPHY_NODE_PAGE_PROP_TITLE, - all); - - ephy_node_set_property_int (eb->priv->pages, - EPHY_NODE_PAGE_PROP_PRIORITY, - EPHY_NODE_ALL_PRIORITY); - - ephy_node_signal_connect_object (eb->priv->pages, - EPHY_NODE_CHILD_ADDED, - (EphyNodeCallback) pages_added_cb, - G_OBJECT (eb)); - ephy_node_signal_connect_object (eb->priv->pages, - EPHY_NODE_CHILD_REMOVED, - (EphyNodeCallback) pages_removed_cb, - G_OBJECT (eb)); - ephy_node_signal_connect_object (eb->priv->pages, - EPHY_NODE_CHILD_CHANGED, - (EphyNodeCallback) pages_changed_cb, - G_OBJECT (eb)); - - /* Hosts */ - eb->priv->hosts = ephy_node_new_with_id (db, HOSTS_NODE_ID); - ephy_node_signal_connect_object (eb->priv->hosts, - EPHY_NODE_CHILD_ADDED, - (EphyNodeCallback) hosts_added_cb, - G_OBJECT (eb)); - ephy_node_signal_connect_object (eb->priv->hosts, - EPHY_NODE_CHILD_REMOVED, - (EphyNodeCallback) hosts_removed_cb, - G_OBJECT (eb)); - ephy_node_signal_connect_object (eb->priv->hosts, - EPHY_NODE_CHILD_CHANGED, - (EphyNodeCallback) hosts_changed_cb, - G_OBJECT (eb)); - - ephy_node_add_child (eb->priv->hosts, eb->priv->pages); - - ephy_node_db_load_from_file (eb->priv->db, eb->priv->xml_file, - EPHY_HISTORY_XML_ROOT, - EPHY_HISTORY_XML_VERSION); - - g_hash_table_foreach (eb->priv->hosts_hash, - (GHFunc) connect_page_removed_from_host, - eb); - - /* mark as clean */ - eb->priv->dirty = FALSE; - - /* setup the periodic history saving callback */ - eb->priv->autosave_timeout = - g_timeout_add_seconds (HISTORY_SAVE_INTERVAL, - (GSourceFunc)periodic_save_cb, - eb); - - disable_history_notifier (NULL, 0, NULL, eb); - eb->priv->disable_history_notifier_id = eel_gconf_notification_add - (CONF_LOCKDOWN_DISABLE_HISTORY, - (GConfClientNotifyFunc) disable_history_notifier, eb); -} - -static void -ephy_history_finalize (GObject *object) -{ - EphyHistory *eb = EPHY_HISTORY (object); - - if (eb->priv->update_hosts_idle) - { - g_source_remove (eb->priv->update_hosts_idle); - } - - ephy_history_save (eb); - - ephy_node_unref (eb->priv->pages); - ephy_node_unref (eb->priv->hosts); - - g_object_unref (eb->priv->db); - - g_hash_table_destroy (eb->priv->pages_hash); - g_hash_table_destroy (eb->priv->hosts_hash); - - g_source_remove (eb->priv->autosave_timeout); - - eel_gconf_notification_remove (eb->priv->disable_history_notifier_id); - - g_free (eb->priv->xml_file); - - LOG ("Global history finalized"); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -EphyHistory * -ephy_history_new (void) -{ - return EPHY_HISTORY (g_object_new (EPHY_TYPE_HISTORY, NULL)); -} - -static void -ephy_history_host_visited (EphyHistory *eh, - EphyNode *host, - GTime now) -{ - int visits; - - LOG ("Host visited"); - - visits = ephy_node_get_property_int - (host, EPHY_NODE_PAGE_PROP_VISITS); - if (visits < 0) visits = 0; - visits++; - - ephy_node_set_property_int (host, EPHY_NODE_PAGE_PROP_VISITS, visits); - ephy_node_set_property_int (host, EPHY_NODE_PAGE_PROP_LAST_VISIT, - now); -} - -static EphyNode * -internal_get_host (EphyHistory *eh, const char *url, gboolean create) -{ - GnomeVFSURI *vfs_uri = NULL; - EphyNode *host = NULL; - const char *host_name = NULL; - GList *host_locations = NULL, *l; - const char *scheme = NULL; - GTime now; - - g_return_val_if_fail (url != NULL, NULL); - - if (eh->priv->enabled == FALSE) - { - return NULL; - } - - now = time (NULL); - - vfs_uri = gnome_vfs_uri_new (url); - - if (vfs_uri) - { - scheme = gnome_vfs_uri_get_scheme (vfs_uri); - host_name = gnome_vfs_uri_get_host_name (vfs_uri); - } - - /* Build an host name */ - if (scheme == NULL || host_name == NULL) - { - host_name = _("Others"); - host_locations = g_list_append (host_locations, - g_strdup ("about:blank")); - } - else if (strcmp (scheme, "file") == 0) - { - host_name = _("Local files"); - host_locations = g_list_append (host_locations, - g_strdup ("file:///")); - } - else - { - char *location; - char *tmp; - - location = g_strconcat (scheme, - "://", host_name, "/", NULL); - host_locations = g_list_append (host_locations, location); - - if (g_str_has_prefix (host_name, "www.")) - { - tmp = g_strdup (g_utf8_offset_to_pointer (host_name, 4)); - } - else - { - tmp = g_strconcat ("www.", host_name, NULL); - } - location = g_strconcat (gnome_vfs_uri_get_scheme (vfs_uri), - "://", tmp, "/", NULL); - g_free (tmp); - host_locations = g_list_append (host_locations, location); - } - - g_return_val_if_fail (host_locations != NULL, NULL); - - for (l = host_locations; l != NULL; l = l->next) - { - host = g_hash_table_lookup (eh->priv->hosts_hash, - (char *)l->data); - if (host) break; - } - - if (!host && create) - { - host = ephy_node_new (eh->priv->db); - ephy_node_signal_connect_object (host, - EPHY_NODE_CHILD_REMOVED, - (EphyNodeCallback) page_removed_from_host_cb, - G_OBJECT (eh)); - ephy_node_signal_connect_object (host, - EPHY_NODE_DESTROY, - (EphyNodeCallback) remove_pages_from_host_cb, - G_OBJECT (eh)); - ephy_node_set_property_string (host, - EPHY_NODE_PAGE_PROP_TITLE, - host_name); - ephy_node_set_property_string (host, - EPHY_NODE_PAGE_PROP_LOCATION, - (char *)host_locations->data); - ephy_node_set_property_int (host, - EPHY_NODE_PAGE_PROP_FIRST_VISIT, - now); - ephy_node_add_child (eh->priv->hosts, host); - } - - if (host) - { - ephy_history_host_visited (eh, host, now); - } - - if (vfs_uri) - { - gnome_vfs_uri_unref (vfs_uri); - } - - g_list_foreach (host_locations, (GFunc)g_free, NULL); - g_list_free (host_locations); - - return host; -} - -EphyNode * -ephy_history_get_host (EphyHistory *eh, const char *url) -{ - return internal_get_host (eh, url, FALSE); -} - -static EphyNode * -ephy_history_add_host (EphyHistory *eh, EphyNode *page) -{ - const char *url; - - url = ephy_node_get_property_string - (page, EPHY_NODE_PAGE_PROP_LOCATION); - - return internal_get_host (eh, url, TRUE); -} - -static void -ephy_history_visited (EphyHistory *eh, EphyNode *node) -{ - GTime now; - int visits; - const char *url; - int host_id; - - now = time (NULL); - - g_assert (node != NULL); - - url = ephy_node_get_property_string - (node, EPHY_NODE_PAGE_PROP_LOCATION); - - visits = ephy_node_get_property_int - (node, EPHY_NODE_PAGE_PROP_VISITS); - if (visits < 0) visits = 0; - visits++; - - ephy_node_set_property_int (node, EPHY_NODE_PAGE_PROP_VISITS, visits); - ephy_node_set_property_int (node, EPHY_NODE_PAGE_PROP_LAST_VISIT, - now); - if (visits == 1) - { - ephy_node_set_property_int - (node, EPHY_NODE_PAGE_PROP_FIRST_VISIT, now); - } - - host_id = ephy_node_get_property_int (node, EPHY_NODE_PAGE_PROP_HOST_ID); - if (host_id >= 0) - { - EphyNode *host; - - host = ephy_node_db_get_node_from_id (eh->priv->db, host_id); - ephy_history_host_visited (eh, host, now); - } - - eh->priv->last_page = node; - - g_signal_emit (G_OBJECT (eh), signals[VISITED], 0, url); -} - -int -ephy_history_get_page_visits (EphyHistory *gh, - const char *url) -{ - EphyNode *node; - int visits = 0; - - node = ephy_history_get_page (gh, url); - if (node) - { - visits = ephy_node_get_property_int - (node, EPHY_NODE_PAGE_PROP_VISITS); - if (visits < 0) visits = 0; - } - - return visits; -} - -void -ephy_history_add_page (EphyHistory *eh, - const char *url, - gboolean redirect, - gboolean toplevel) -{ - gboolean result = FALSE; - - g_signal_emit (eh, signals[ADD_PAGE], 0, url, redirect, toplevel, &result); -} - -static gboolean -impl_add_page (EphyHistory *eb, - const char *url, - gboolean redirect, - gboolean toplevel) -{ - EphyNode *bm, *node, *host; - gulong flags = 0; - - if (eb->priv->enabled == FALSE) - { - return FALSE; - } - - node = ephy_history_get_page (eb, url); - if (node) - { - ephy_history_visited (eb, node); - return TRUE; - } - - bm = ephy_node_new (eb->priv->db); - - ephy_node_set_property_string (bm, EPHY_NODE_PAGE_PROP_LOCATION, url); - ephy_node_set_property_string (bm, EPHY_NODE_PAGE_PROP_TITLE, url); - - if (redirect) flags |= REDIRECT_FLAG; - if (toplevel) flags |= TOPLEVEL_FLAG; - - /* EphyNode SUCKS! */ - ephy_node_set_property_long (bm, EPHY_NODE_PAGE_PROP_EXTRA_FLAGS, - flags); - - host = ephy_history_add_host (eb, bm); - - ephy_node_set_property_int (bm, EPHY_NODE_PAGE_PROP_HOST_ID, - ephy_node_get_id (host)); - - ephy_history_visited (eb, bm); - - ephy_node_add_child (host, bm); - ephy_node_add_child (eb->priv->pages, bm); - - return TRUE; -} - -EphyNode * -ephy_history_get_page (EphyHistory *eb, - const char *url) -{ - EphyNode *node; - - node = g_hash_table_lookup (eb->priv->pages_hash, url); - - return node; -} - -gboolean -ephy_history_is_page_visited (EphyHistory *gh, - const char *url) -{ - return (ephy_history_get_page (gh, url) != NULL); -} - -void -ephy_history_set_page_title (EphyHistory *gh, - const char *url, - const char *title) -{ - EphyNode *node; - - LOG ("Set page title"); - - if (title == NULL || title[0] == '\0') return; - - node = ephy_history_get_page (gh, url); - if (node == NULL) return; - - ephy_node_set_property_string (node, EPHY_NODE_PAGE_PROP_TITLE, - title); -} - -const char* -ephy_history_get_icon (EphyHistory *gh, - const char *url) -{ - EphyNode *node, *host; - int host_id; - - node = ephy_history_get_page (gh, url); - if (node == NULL) return NULL; - - host_id = ephy_node_get_property_int (node, EPHY_NODE_PAGE_PROP_HOST_ID); - g_return_val_if_fail (host_id >= 0, NULL); - - host = ephy_node_db_get_node_from_id (gh->priv->db, host_id); - g_return_val_if_fail (host != NULL, NULL); - - return ephy_node_get_property_string (host, EPHY_NODE_PAGE_PROP_ICON); -} - - -void -ephy_history_set_icon (EphyHistory *gh, - const char *url, - const char *icon) -{ - EphyNode *host; - - LOG ("Set host icon"); - - host = g_hash_table_lookup (gh->priv->hosts_hash, url); - if (host) - { - ephy_node_set_property_string (host, EPHY_NODE_PAGE_PROP_ICON, - icon); - } -} - -void -ephy_history_clear (EphyHistory *gh) -{ - EphyNode *node; - - LOG ("clearing history"); - - ephy_node_db_set_immutable (gh->priv->db, FALSE); - - while ((node = ephy_node_get_nth_child (gh->priv->pages, 0)) != NULL) - { - ephy_node_unref (node); - } - ephy_history_save (gh); - - ephy_node_db_set_immutable (gh->priv->db, !gh->priv->enabled); - - g_signal_emit (gh, signals[CLEARED], 0); -} - -EphyNode * -ephy_history_get_hosts (EphyHistory *eb) -{ - return eb->priv->hosts; -} - -EphyNode * -ephy_history_get_pages (EphyHistory *eb) -{ - return eb->priv->pages; -} - -const char * -ephy_history_get_last_page (EphyHistory *gh) -{ - if (gh->priv->last_page == NULL) return NULL; - - return ephy_node_get_property_string - (gh->priv->last_page, EPHY_NODE_PAGE_PROP_LOCATION); -} - -gboolean -ephy_history_is_enabled (EphyHistory *history) -{ - g_return_val_if_fail (EPHY_IS_HISTORY (history), FALSE); - - return history->priv->enabled; -} +#include +#include +#include + +#include "ephy-history.h" + +G_DEFINE_TYPE(EphyHistory, ephy_history, G_TYPE_OBJECT) +#define EPHY_HISTORY_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_HISTORY, EphyHistoryPrivate)) + +#define EPHY_DB_PATH "/tmp/ephy-history.sqlite" +#define ephy_db_return_on_error(db, rc, err) do { \ + if (rc != SQLITE_OK) \ + { \ + g_set_error (err, \ + EPHY_DB_ERROR_QUARK, \ + rc, \ + sqlite3_errmsg(db)); \ + return FALSE; \ + } \ +} while(0) + +struct _EphyHistoryPrivate +{ + sqlite3 *db; + gboolean enabled; + + /* pre-compiled queries */ + sqlite3_stmt *q_get_hosts; + sqlite3_stmt *q_get_pages; + sqlite3_stmt *q_get_page_by_id; + + sqlite3_stmt *q_get_page_info; + sqlite3_stmt *q_get_host_info; + sqlite3_stmt *q_get_favicon; + sqlite3_stmt *q_get_page_visit_info; + sqlite3_stmt *q_get_page_icon; + sqlite3_stmt *q_get_page_id; + sqlite3_stmt *q_get_site_id; + sqlite3_stmt *q_get_favicon_id; + sqlite3_stmt *q_add_page; + sqlite3_stmt *q_add_site; + sqlite3_stmt *q_add_favicon; + sqlite3_stmt *q_add_visit; + sqlite3_stmt *q_update_page_visit_info; + sqlite3_stmt *q_update_page_title; + sqlite3_stmt *q_update_page_icon; +}; + +enum +{ + PROP_0, + PROP_ENABLED +}; + +static void ephy_history_finalize (GObject *object); + +/* Private functions */ +static gboolean db_init(EphyHistory *eh, GError **err); +static gboolean db_init_queries(EphyHistory *eh, GError **err); +static gboolean db_table_exists(sqlite3 *db, const char *tblname, + gboolean *exists, GError **err); +static gboolean db_close(EphyHistory *eh, GError **err); + +static gboolean get_favicon_id(EphyHistory *eh, const char *url, + gint64 *favicon_id); +static gboolean add_favicon(EphyHistory *eh, const char *url, + gint64 *favicon_id); +static gboolean update_page_icon(EphyHistory *eh, const char *url, gint icon_id); + +static gboolean get_page_visit_info(EphyHistory *eh, const char *url, + gint64 *page_id, int *visit_count); +static gboolean update_page_visit_info(EphyHistory *eh, gint64 page_id, + int visit_count); +static gboolean add_page(EphyHistory *eh, const char *url, const char *title, + int visit_count, gint64 *page_id); +static gboolean add_site(EphyHistory *eh, const char *url, gint64 *site_id); +static gboolean add_visit(EphyHistory *eh, gint64 page_id, guint64 timestamp, + const char *ref_url, int trans_type, + int visit_duration, gint64 *visit_id); + +static gboolean update_page_title(EphyHistory *eh, const char *url, + const char *title); + +/* utility functions */ +static guint64 get_current_time(); +static char *get_hostname_from_url(const char *url); + +GQuark ephy_db_error_quark; + +static void +ephy_history_set_enabled (EphyHistory *history, + gboolean enabled) +{ + history->priv->enabled = enabled; +} + +static void +ephy_history_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EphyHistory *history = EPHY_HISTORY (object); + + switch (prop_id) + { + case PROP_ENABLED: + ephy_history_set_enabled (history, g_value_get_boolean (value)); + break; + } +} + +static void +ephy_history_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EphyHistory *history = EPHY_HISTORY (object); + + switch (prop_id) + { + case PROP_ENABLED: + g_value_set_boolean (value, history->priv->enabled); + break; + } +} + +static void +ephy_history_class_init (EphyHistoryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = ephy_history_finalize; + + object_class->get_property = ephy_history_get_property; + object_class->set_property = ephy_history_set_property; + + g_object_class_install_property (object_class, + PROP_ENABLED, + g_param_spec_boolean ("enabled", + "Enabled", + "Enabled", + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_type_class_add_private (object_class, sizeof (EphyHistoryPrivate)); +} + +static void +ephy_history_finalize (GObject *object) +{ + EphyHistory *eh = EPHY_HISTORY (object); + db_close (eh, NULL); + + G_OBJECT_CLASS (ephy_history_parent_class)->finalize (object); +} + +static void +ephy_history_init (EphyHistory *eh) +{ + eh->priv = EPHY_HISTORY_GET_PRIVATE (eh); +} + +/* + * Private + */ + +static gboolean +db_table_exists(sqlite3 *db, const char *tblname, gboolean *exists, GError **err) +{ + sqlite3_stmt *stmt; + int rc; + gchar *query; + + query = g_strdup_printf("SELECT name FROM sqlite_master WHERE " + "type = 'table' AND name = '%s'", tblname); + rc = sqlite3_prepare_v2(db, query, -1, &stmt, NULL); + g_free(query); + ephy_db_return_on_error(db, rc, err); + rc = sqlite3_step(stmt); + if(rc == SQLITE_ROW) { + *exists = TRUE; + } + else if(rc == SQLITE_DONE) { + *exists = FALSE; + } + else if(rc == SQLITE_ERROR) { + g_set_error (err, + EPHY_DB_ERROR_QUARK, + rc, + sqlite3_errmsg(db)); + return FALSE; + } + rc = sqlite3_finalize(stmt); + return TRUE; +} + +static gboolean +db_close(EphyHistory *eh, GError **err) +{ + sqlite3 *db = eh->priv->db; + int rc; + + rc = sqlite3_finalize(eh->priv->q_get_hosts); + rc = sqlite3_finalize(eh->priv->q_get_pages); + rc = sqlite3_finalize(eh->priv->q_get_page_info); + rc = sqlite3_finalize(eh->priv->q_get_host_info); + rc = sqlite3_finalize(eh->priv->q_get_favicon); + rc = sqlite3_finalize(eh->priv->q_get_page_visit_info); + rc = sqlite3_finalize(eh->priv->q_get_page_icon); + rc = sqlite3_finalize(eh->priv->q_get_page_id); + rc = sqlite3_finalize(eh->priv->q_get_site_id); + rc = sqlite3_finalize(eh->priv->q_get_favicon_id); + rc = sqlite3_finalize(eh->priv->q_add_page); + rc = sqlite3_finalize(eh->priv->q_add_site); + rc = sqlite3_finalize(eh->priv->q_add_favicon); + rc = sqlite3_finalize(eh->priv->q_add_visit); + rc = sqlite3_finalize(eh->priv->q_update_page_visit_info); + rc = sqlite3_finalize(eh->priv->q_update_page_title); + rc = sqlite3_finalize(eh->priv->q_update_page_icon); + + rc = sqlite3_close(db); + ephy_db_return_on_error(db, rc, err); + + return TRUE; +} + +static gboolean +db_init(EphyHistory *eh, GError **err) +{ + sqlite3 *db; + int rc; + gboolean tbl_exists; + + rc = sqlite3_open(EPHY_DB_PATH, &db); + ephy_db_return_on_error(db, rc, err); + eh->priv->db = db; + + /* create tables */ + if(db_table_exists(db, "ephy_pages", &tbl_exists, err) == FALSE) { + return FALSE; + } + if(!tbl_exists) { + g_debug("No ephy_pages table"); + rc = sqlite3_exec(db, + "CREATE TABLE ephy_pages (" + "id INTEGER PRIMARY KEY," + "url LONGVARCHAR," + "title LONGVARCHAR," + "host_id INTEGER," + "favicon_id INTEGER," + "visit_count INTEGER DEFAULT 0)", + NULL, NULL, NULL); + ephy_db_return_on_error(db, rc, err); + } + else { + g_debug("ephy_pages table exists"); + } + if(db_table_exists(db, "ephy_sites", &tbl_exists, err) == FALSE) { + return FALSE; + } + if(!tbl_exists) { + g_debug("No ephy_sites table"); + rc = sqlite3_exec(db, + "CREATE TABLE ephy_sites (" + "id INTEGER PRIMARY KEY," + "url LONGVARCHAR," + "title LONGVARCHAR," + "favicon_id INTEGER)", + NULL, NULL, NULL); + ephy_db_return_on_error(db, rc, err); + } + else { + g_debug("ephy_sites table exists"); + } + if(db_table_exists(db, "ephy_favicons", &tbl_exists, err) == FALSE) { + return FALSE; + } + if(!tbl_exists) { + g_debug("No ephy_favicons table"); + rc = sqlite3_exec(db, + "CREATE TABLE ephy_favicons (" + "id INTEGER PRIMARY KEY," + "url LONGVARCHAR)", + NULL, NULL, NULL); + ephy_db_return_on_error(db, rc, err); + } + else { + g_debug("ephy_favicons table exists"); + } + if(db_table_exists(db, "ephy_pagevisits", &tbl_exists, err) == FALSE) { + return FALSE; + } + if(!tbl_exists) { + g_debug("No ephy_pagevisits table"); + rc = sqlite3_exec(db, + "CREATE TABLE ephy_pagevisits (" + "id INTEGER PRIMARY KEY," + "page_id INTEGER," + "visit_date INTEGER," + "visit_ref INTEGER," + "visit_type INTEGER," + "visit_duration INTEGER)", + NULL, NULL, NULL); + ephy_db_return_on_error(db, rc, err); + } + else { + g_debug("ephy_pagevisits table exists"); + } + + return TRUE; +} + +static gboolean +db_init_queries(EphyHistory *eh, GError **err) +{ + sqlite3 *db; + int rc; + + db = eh->priv->db; + /* All Sites */ + rc = sqlite3_prepare_v2(db, "SELECT s.id, s.url, s.title, COUNT(*), " + "MAX(visit_date), f.url FROM ephy_sites s JOIN " + "ephy_pages p ON p.host_id = s.id JOIN " + "ephy_pagevisits v ON p.id = v.page_id " + "LEFT OUTER JOIN ephy_favicons f ON " + "f.id = s.favicon_id GROUP BY s.id", -1, + &eh->priv->q_get_hosts, NULL); + ephy_db_return_on_error(db, rc, err); + + /* All Pages */ + rc = sqlite3_prepare_v2(db, "SELECT p.id, p.url, p.title, p.visit_count, " + "(SELECT MAX(visit_date) AS last_visit FROM " + "ephy_pagevisits WHERE page_id = p.id), " + "f.url FROM ephy_pages p LEFT OUTER JOIN " + "ephy_favicons f ON p.favicon_id = f.id", -1, + &eh->priv->q_get_pages, NULL); + ephy_db_return_on_error(db, rc, err); + + /* All Pages */ + rc = sqlite3_prepare_v2(db, "SELECT p.id, p.url, p.title, p.visit_count, " + "(SELECT MAX(visit_date) AS last_visit FROM " + "ephy_pagevisits WHERE page_id = p.id), " + "f.url FROM ephy_pages p LEFT OUTER JOIN " + "ephy_favicons f ON p.favicon_id = f.id " + "WHERE p.id = ?1", -1, + &eh->priv->q_get_page_by_id, NULL); + ephy_db_return_on_error(db, rc, err); + + /* Get Icon */ + rc = sqlite3_prepare_v2(db, "SELECT f.url FROM ephy_favicons f,ephy_pages p " + "WHERE p.url = ?1 AND p.favicon_id = f.id", -1, + &eh->priv->q_get_favicon, NULL); + ephy_db_return_on_error(db, rc, err); + + /* Set Icon */ + rc = sqlite3_prepare_v2(db, "SELECT id FROM ephy_favicons " + " WHERE url = ?1", -1, + &eh->priv->q_get_favicon_id, NULL); + ephy_db_return_on_error(db, rc, err); + + rc = sqlite3_prepare_v2(db, "INSERT INTO ephy_favicons " + "(url) VALUES (?1)", -1, + &eh->priv->q_add_favicon, NULL); + ephy_db_return_on_error(db, rc, err); + + rc = sqlite3_prepare_v2(db, "UPDATE ephy_pages SET favicon_id = ?1 " + "WHERE url = ?2", -1, + &eh->priv->q_update_page_icon, NULL); + ephy_db_return_on_error(db, rc, err); + + /* Add Page */ + rc = sqlite3_prepare_v2(db, "SELECT id, visit_count FROM ephy_pages " + "WHERE url = ?1", -1, + &eh->priv->q_get_page_visit_info, NULL); + ephy_db_return_on_error(db, rc, err); + + rc = sqlite3_prepare_v2(db, "UPDATE ephy_pages SET visit_count = ?1 " + "WHERE id = ?2", -1, + &eh->priv->q_update_page_visit_info, NULL); + ephy_db_return_on_error(db, rc, err); + + rc = sqlite3_prepare_v2(db, "INSERT INTO ephy_pages " + "(url, title, visit_count, host_id) " + "VALUES (?1, ?2, ?3, ?4)", -1, + &eh->priv->q_add_page, NULL); + ephy_db_return_on_error(db, rc, err); + + rc = sqlite3_prepare_v2(db, "INSERT INTO ephy_sites " + "(url) VALUES (?1)", -1, + &eh->priv->q_add_site, NULL); + ephy_db_return_on_error(db, rc, err); + + rc = sqlite3_prepare_v2(db, "INSERT INTO ephy_pagevisits " + "(page_id, visit_date, visit_ref, " + "visit_type, visit_duration) " + "VALUES (?1, ?2, ?3, ?4, ?5)", -1, + &eh->priv->q_add_visit, NULL); + ephy_db_return_on_error(db, rc, err); + + /* */ + rc = sqlite3_prepare_v2(db, "SELECT url, title, visit_count, host_id, " + "favicon_id FROM ephy_pages WHERE url = ?1", -1, + &eh->priv->q_get_page_info, NULL); + ephy_db_return_on_error(db, rc, err); + rc = sqlite3_prepare_v2(db, "SELECT url, favicon_id " + "FROM ephy_sites WHERE url = ?1", -1, + &eh->priv->q_get_host_info, NULL); + ephy_db_return_on_error(db, rc, err); + rc = sqlite3_prepare_v2(db, "SELECT id FROM ephy_pages " + "WHERE url = ?1", -1, + &eh->priv->q_get_page_id, NULL); + ephy_db_return_on_error(db, rc, err); + rc = sqlite3_prepare_v2(db, "SELECT id FROM ephy_sites " + "WHERE url = ?1", -1, + &eh->priv->q_get_site_id, NULL); + ephy_db_return_on_error(db, rc, err); + + /* Update Page Title */ + rc = sqlite3_prepare_v2(db, "UPDATE ephy_pages SET title = ?1 " + "WHERE url = ?2", -1, + &eh->priv->q_update_page_title, NULL); + ephy_db_return_on_error(db, rc, err); + return TRUE; +} + +static guint64 +get_current_time() +{ + guint64 time_now; + GTimeVal tv; + + g_get_current_time(&tv); + time_now = (guint64) tv.tv_sec * G_USEC_PER_SEC + tv.tv_usec; + return time_now; +} + +static char +*get_hostname_from_url(const char *url) +{ + GnomeVFSURI *vfs_uri = NULL; + const char *host_name = NULL; + const char *scheme = NULL; + char *host_url = NULL; + + vfs_uri = gnome_vfs_uri_new(url); + if(vfs_uri != NULL) { + scheme = gnome_vfs_uri_get_scheme(vfs_uri); + host_name = gnome_vfs_uri_get_host_name(vfs_uri); + if(scheme == NULL) + scheme = "Unknown"; + if(host_name != NULL) + host_url = g_strconcat(scheme, "://", host_name, "/", NULL); + } + + return host_url; +}; + +static gboolean +get_page_visit_info(EphyHistory *eh, const char *url, + gint64 *page_id, int *visit_count) +{ + int rc; + + rc = sqlite3_bind_text(eh->priv->q_get_page_visit_info, 1, url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_step(eh->priv->q_get_page_visit_info); + if (rc != SQLITE_ROW) { + sqlite3_reset(eh->priv->q_get_page_visit_info); + return FALSE; + } + *page_id = sqlite3_column_int64(eh->priv->q_get_page_visit_info, 0); + *visit_count = sqlite3_column_int(eh->priv->q_get_page_visit_info, 1); + rc = sqlite3_reset(eh->priv->q_get_page_visit_info); + ephy_db_return_on_error(eh->priv->db, rc, NULL); + return TRUE; +} + +static gboolean +get_page_id(EphyHistory *eh, const char *url, gint64 *page_id) +{ + int rc; + + rc = sqlite3_bind_text(eh->priv->q_get_page_id, 1, url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_step(eh->priv->q_get_page_id); + if (rc != SQLITE_ROW) { + sqlite3_reset(eh->priv->q_get_page_id); + return FALSE; + } + *page_id = sqlite3_column_int64(eh->priv->q_get_page_id, 0); + sqlite3_reset(eh->priv->q_get_page_id); + return TRUE; +} + +static gboolean +get_site_id(EphyHistory *eh, const char *url, gint64 *site_id) +{ + int rc; + + rc = sqlite3_bind_text(eh->priv->q_get_site_id, 1, url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_step(eh->priv->q_get_site_id); + if (rc != SQLITE_ROW) { + sqlite3_reset(eh->priv->q_get_site_id); + return FALSE; + } + *site_id = sqlite3_column_int64(eh->priv->q_get_site_id, 0); + sqlite3_reset(eh->priv->q_get_site_id); + return TRUE; +} + +static gboolean +get_favicon_id(EphyHistory *eh, const char *url, gint64 *favicon_id) +{ + int rc; + + rc = sqlite3_bind_text(eh->priv->q_get_favicon_id, 1, url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_step(eh->priv->q_get_favicon_id); + if (rc != SQLITE_ROW) { + sqlite3_reset(eh->priv->q_get_favicon_id); + return FALSE; + } + *favicon_id = sqlite3_column_int64(eh->priv->q_get_favicon_id, 0); + sqlite3_reset(eh->priv->q_get_favicon_id); + return TRUE; +} + +static gboolean +update_page_title(EphyHistory *eh, const char *url, const char *title) +{ + int rc; + + /* XXX: update only if title changed */ + rc = sqlite3_bind_text(eh->priv->q_update_page_title, 1, title, -1, + SQLITE_TRANSIENT); + rc = sqlite3_bind_text(eh->priv->q_update_page_title, 2, url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_step(eh->priv->q_update_page_title); + sqlite3_reset(eh->priv->q_update_page_title); + if (rc != SQLITE_DONE) + return FALSE; + return TRUE; +} + +static gboolean +update_page_icon(EphyHistory *eh, const char *url, gint icon_id) +{ + int rc; + + /* XXX: update only if icon changed */ + rc = sqlite3_bind_int64(eh->priv->q_update_page_icon, 1, icon_id); + rc = sqlite3_bind_text(eh->priv->q_update_page_icon, 2, url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_step(eh->priv->q_update_page_icon); + sqlite3_reset(eh->priv->q_update_page_icon); + if (rc != SQLITE_DONE) + return FALSE; + return TRUE; +} + +static gboolean +update_page_visit_info(EphyHistory *eh, gint64 page_id, int visit_count) +{ + int rc; + + rc = sqlite3_bind_int(eh->priv->q_update_page_visit_info, 1, + visit_count); + rc = sqlite3_bind_int64(eh->priv->q_update_page_visit_info, 2, + page_id); + rc = sqlite3_step(eh->priv->q_update_page_visit_info); + sqlite3_reset(eh->priv->q_update_page_visit_info); + ephy_db_return_on_error(eh->priv->db, rc, NULL); + return TRUE; +} + +static gboolean +add_page(EphyHistory *eh, const char *url, const char *title, + int visit_count, gint64 *page_id) +{ + int rc; + gint64 site_id; + char *host_name; + + host_name = get_hostname_from_url(url); + if(!get_site_id(eh, host_name, &site_id)) { + g_debug("site not in table"); + rc = add_site(eh, host_name, &site_id); + } + g_free(host_name); + + rc = sqlite3_bind_text(eh->priv->q_add_page, 1, url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_bind_text(eh->priv->q_add_page, 2, title, -1, + SQLITE_TRANSIENT); + rc = sqlite3_bind_int(eh->priv->q_add_page, 3, visit_count); + rc = sqlite3_bind_int64(eh->priv->q_add_page, 4, site_id); + rc = sqlite3_step(eh->priv->q_add_page); + sqlite3_reset(eh->priv->q_add_page); + if (rc != SQLITE_DONE) + return FALSE; + *page_id = sqlite3_last_insert_rowid(eh->priv->db); + return TRUE; +} + +static gboolean +add_site(EphyHistory *eh, const char *url, gint64 *site_id) +{ + int rc; + + rc = sqlite3_bind_text(eh->priv->q_add_site, 1, url, -1, SQLITE_TRANSIENT); + rc = sqlite3_step(eh->priv->q_add_site); + sqlite3_reset(eh->priv->q_add_site); + if (rc != SQLITE_DONE) + return FALSE; + *site_id = sqlite3_last_insert_rowid(eh->priv->db); + return TRUE; +} + +static gboolean +add_favicon(EphyHistory *eh, const char *url, gint64 *favicon_id) +{ + int rc; + + rc = sqlite3_bind_text(eh->priv->q_add_favicon, 1, url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_step(eh->priv->q_add_favicon); + sqlite3_reset(eh->priv->q_add_favicon); + if (rc != SQLITE_DONE) + return FALSE; + *favicon_id = sqlite3_last_insert_rowid(eh->priv->db); + return TRUE; +} + +static gboolean +add_visit(EphyHistory *eh, gint64 page_id, guint64 timestamp, + const char *ref_url, int trans_type, int visit_duration, + gint64 *visit_id) +{ + int rc; + + rc = sqlite3_bind_int64(eh->priv->q_add_visit, 1, page_id); + rc = sqlite3_bind_int64(eh->priv->q_add_visit, 2, timestamp); + rc = sqlite3_bind_text(eh->priv->q_add_visit, 3, ref_url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_bind_int(eh->priv->q_add_visit, 4, trans_type); + rc = sqlite3_bind_int(eh->priv->q_add_visit, 5, visit_duration); + rc = sqlite3_step(eh->priv->q_add_visit); + sqlite3_reset(eh->priv->q_add_visit); + if (rc != SQLITE_DONE) + return FALSE; + *visit_id = sqlite3_last_insert_rowid(eh->priv->db); + return TRUE; +} + + +/* + * Public API + */ + +/* + * ephy-history compat API + */ + +EphyHistory * +ephy_history_new (void) +{ + EphyHistory *eh; + + eh = EPHY_HISTORY (g_object_new (EPHY_TYPE_HISTORY, NULL)); + ephy_db_error_quark = g_quark_from_static_string ("ephy-db-error"); + + db_init(eh, NULL); + db_init_queries(eh, NULL); + + return eh; +} + +gboolean +ephy_history_is_enabled (EphyHistory *eh) +{ + g_return_val_if_fail (EPHY_IS_HISTORY (eh), FALSE); + + return eh->priv->enabled; +} + +gboolean +ephy_history_get_page_visit_count (EphyHistory *eh, + const char *url, + int *visit_count) +{ + gint64 page_id; + + if(get_page_visit_info(eh, url, &page_id, visit_count)) + return TRUE; + + return FALSE; +} + +GPtrArray * +ephy_history_get_hosts (EphyHistory *eh) +{ + GPtrArray *hosts; + EphyHistoryPageNode *host = NULL; + int rc; + gint64 id, last_visit; + guint visit_count; + gchar *uri, *title, *favicon_uri; + + hosts = g_ptr_array_new(); + while ((rc = sqlite3_step(eh->priv->q_get_hosts)) == SQLITE_ROW) { + id = sqlite3_column_int64(eh->priv->q_get_hosts, 0); + uri = g_strdup(sqlite3_column_text(eh->priv->q_get_hosts, 1)); + title = g_strdup(sqlite3_column_text(eh->priv->q_get_hosts, 2)); + visit_count = sqlite3_column_int(eh->priv->q_get_hosts, 3); + last_visit = sqlite3_column_int64(eh->priv->q_get_hosts, 4); + favicon_uri = g_strdup(sqlite3_column_text(eh->priv->q_get_hosts, 5)); + /* + g_debug("get_hosts: %llu %s %s %s %d %llu", id, uri, title, favicon_uri, + visit_count, last_visit); + */ + host = ephy_history_page_node_new(id, uri, title, favicon_uri, + visit_count, last_visit); + g_ptr_array_add (hosts, host); + } + if (rc != SQLITE_DONE) { + /* XXX: right now we return whatever we got! */ + } + sqlite3_reset(eh->priv->q_get_hosts); + return hosts; +} + +GPtrArray * +ephy_history_get_pages (EphyHistory *eh) +{ + GPtrArray *pages; + EphyHistoryPageNode *page = NULL; + int rc; + gint64 id, last_visit; + guint visit_count; + gchar *uri, *title, *favicon_uri; + + pages = g_ptr_array_new(); + while ((rc = sqlite3_step(eh->priv->q_get_pages)) == SQLITE_ROW) { + id = sqlite3_column_int64(eh->priv->q_get_pages, 0); + uri = g_strdup(sqlite3_column_text(eh->priv->q_get_pages, 1)); + title = g_strdup(sqlite3_column_text(eh->priv->q_get_pages, 2)); + visit_count = sqlite3_column_int(eh->priv->q_get_pages, 3); + last_visit = sqlite3_column_int64(eh->priv->q_get_pages, 4); + favicon_uri = g_strdup(sqlite3_column_text(eh->priv->q_get_pages, 5)); + /* + g_debug("get_pages: %llu %s %s %s %d %llu", id, uri, title, favicon_uri, + visit_count, last_visit); + */ + page = ephy_history_page_node_new(id, uri, title, favicon_uri, + visit_count, last_visit); + g_ptr_array_add (pages, page); + } + if (rc != SQLITE_DONE) { + /* XXX: right now we return whatever we got! */ + } + sqlite3_reset(eh->priv->q_get_pages); + return pages; +} + +gboolean +ephy_history_set_icon (EphyHistory *eh, const char *url, const char *icon_url) +{ + gint64 icon_id; + int rc; + + if(!get_favicon_id(eh, icon_url, &icon_id)) { + g_debug("icon not in table"); + rc = add_favicon(eh, icon_url, &icon_id); + } + rc = update_page_icon(eh, url, icon_id); + + return TRUE; +} + +const char* +ephy_history_get_icon (EphyHistory *eh, const char *url) +{ + char *icon_url = NULL; + int rc; + + rc = sqlite3_bind_text(eh->priv->q_get_favicon, 1, url, -1, + SQLITE_TRANSIENT); + rc = sqlite3_step(eh->priv->q_get_favicon); + if (rc == SQLITE_ROW) + icon_url = g_strdup(sqlite3_column_text(eh->priv->q_get_favicon, 0)); + + rc = sqlite3_reset(eh->priv->q_get_favicon); + return icon_url; +} + +/* + * nsIGlobalHistory2 + */ + +gboolean +ephy_history_add_page (EphyHistory *eh, const char *url, gboolean redirect, + gboolean toplevel, const char *ref_url) +{ + gint64 page_id; + int visit_count; + gint64 visit_id; + int rc; + + if (eh->priv->enabled == FALSE) { + return FALSE; + } + if(get_page_visit_info(eh, url, &page_id, &visit_count)) { + g_debug("page in table"); + update_page_visit_info(eh, page_id, visit_count + 1); + } + /* not present */ + else { + g_debug("page NOT in table"); + add_page(eh, url, NULL, 1, &page_id); + } + add_visit(eh, page_id, get_current_time(), ref_url, 0, 0, &visit_id); + + return TRUE; +} + +gboolean +ephy_history_is_page_visited (EphyHistory *eh, const char *url) +{ + gint64 page_id; + int visit_count; + + if(get_page_visit_info(eh, url, &page_id, &visit_count) && visit_count > 0) + return TRUE; + + return FALSE; +} + +gboolean +ephy_history_set_page_title (EphyHistory *eh, const char *url, + const char *title) +{ + /* XXX: don't update if title is same as the existing one */ + return update_page_title(eh, url, title); +} + +/* + * Extra! Extra! + */ + +/* All Page IDs: for auto-completion */ + +GArray * +ephy_history_get_page_ids (EphyHistory *eh) +{ + GArray *page_ids = NULL; + char **ids; + char *err_msg; + int nrows; + int ncols; + guint id; + int rc; + int i; + + rc = sqlite3_get_table (eh->priv->db, + "SELECT p.id FROM ephy_pages p ORDER BY id", + &ids, &nrows, &ncols, &err_msg); + if (rc == SQLITE_OK) { + page_ids = g_array_sized_new (FALSE, FALSE, sizeof(guint), nrows); + for (i = 1; i < nrows + 1; i++) { + id = strtol(ids[i], NULL, 10); + g_array_append_val (page_ids, id); + } + sqlite3_free_table (ids); + } + else { + g_critical ("get_page_ids: %s\n", err_msg); + sqlite3_free (err_msg); + } + return page_ids; +} + +EphyHistoryPageNode * +ephy_history_get_page_by_id (EphyHistory *eh, guint64 id) +{ + EphyHistoryPageNode *page; + int rc; + gint64 last_visit; + guint visit_count; + gchar *uri, *title, *favicon_uri; + + rc = sqlite3_bind_int64(eh->priv->q_get_page_by_id, 1, id); + rc = sqlite3_step(eh->priv->q_get_page_by_id); + if (rc == SQLITE_ROW) { + id = sqlite3_column_int64(eh->priv->q_get_page_by_id, 0); + uri = g_strdup(sqlite3_column_text(eh->priv->q_get_page_by_id, 1)); + title = g_strdup(sqlite3_column_text(eh->priv->q_get_page_by_id, 2)); + visit_count = sqlite3_column_int(eh->priv->q_get_page_by_id, 3); + last_visit = sqlite3_column_int64(eh->priv->q_get_page_by_id, 4); + favicon_uri = g_strdup(sqlite3_column_text(eh->priv->q_get_page_by_id, + 5)); + page = ephy_history_page_node_new(id, uri, title, favicon_uri, + visit_count, last_visit); + } + + rc = sqlite3_reset(eh->priv->q_get_page_by_id); + return page; +} + + + diff --git a/embed/ephy-history.h b/embed/ephy-history.h dissimilarity index 94% index ca40304..d3dc053 100644 --- a/embed/ephy-history.h +++ b/embed/ephy-history.h @@ -1,125 +1,99 @@ -/* - * Copyright © 2000-2003 Marco Pesenti Gritti - * - * This program 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; either version 2, or (at your option) - * any later version. - * - * This program 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * $Id: ephy-history.h 6952 2007-03-11 19:42:02Z chpe $ - */ - -#ifndef EPHY_HISTORY_H -#define EPHY_HISTORY_H - -#include - -#include "ephy-node.h" - -G_BEGIN_DECLS - -#define EPHY_TYPE_HISTORY (ephy_history_get_type ()) -#define EPHY_HISTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_HISTORY, EphyHistory)) -#define EPHY_HISTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_HISTORY, EphyHistoryClass)) -#define EPHY_IS_HISTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_HISTORY)) -#define EPHY_IS_HISTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_HISTORY)) -#define EPHY_HISTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_HISTORY, EphyHistoryClass)) - -typedef struct _EphyHistoryClass EphyHistoryClass; -typedef struct _EphyHistory EphyHistory; -typedef struct _EphyHistoryPrivate EphyHistoryPrivate; - -enum -{ - EPHY_NODE_PAGE_PROP_TITLE = 2, - EPHY_NODE_PAGE_PROP_LOCATION = 3, - EPHY_NODE_PAGE_PROP_VISITS = 4, - EPHY_NODE_PAGE_PROP_LAST_VISIT = 5, - EPHY_NODE_PAGE_PROP_FIRST_VISIT = 6, - EPHY_NODE_PAGE_PROP_HOST_ID = 7, - EPHY_NODE_PAGE_PROP_PRIORITY = 8, - EPHY_NODE_PAGE_PROP_ICON = 9, - EPHY_NODE_HOST_PROP_ZOOM = 10, - EPHY_NODE_PAGE_PROP_GECKO_FLAGS = 11, - EPHY_NODE_PAGE_PROP_EXTRA_FLAGS = 12 -}; - -struct _EphyHistory -{ - GObject parent; - - /*< private >*/ - EphyHistoryPrivate *priv; -}; - -struct _EphyHistoryClass -{ - GObjectClass parent_class; - - /* Signals */ - gboolean (* add_page) (EphyHistory *history, - const char *url, - gboolean redirect, - gboolean toplevel); - void (* visited) (EphyHistory *history, - const char *url); - void (* cleared) (EphyHistory *history); - - void (* redirect) (EphyHistory *history, - const char *from_uri, - const char *to_uri); -}; - -GType ephy_history_get_type (void); - -EphyHistory *ephy_history_new (void); - -EphyNode *ephy_history_get_hosts (EphyHistory *gh); - -EphyNode *ephy_history_get_host (EphyHistory *gh, - const char *url); - -EphyNode *ephy_history_get_pages (EphyHistory *gh); - -EphyNode *ephy_history_get_page (EphyHistory *gh, - const char *url); - -void ephy_history_add_page (EphyHistory *gh, - const char *url, - gboolean redirect, - gboolean toplevel); - -gboolean ephy_history_is_page_visited (EphyHistory *gh, - const char *url); - -int ephy_history_get_page_visits (EphyHistory *gh, - const char *url); - -void ephy_history_set_page_title (EphyHistory *gh, - const char *url, - const char *title); - -const char *ephy_history_get_last_page (EphyHistory *gh); - -void ephy_history_set_icon (EphyHistory *gh, - const char *url, - const char *icon); -const char *ephy_history_get_icon (EphyHistory *gh, - const char *url); - -void ephy_history_clear (EphyHistory *gh); - -gboolean ephy_history_is_enabled (EphyHistory *history); - -G_END_DECLS - -#endif +#ifndef EPHY_HISTORY_H +#define EPHY_HISTORY_H + +#include +#include "ephy-history-page-node.h" + +G_BEGIN_DECLS + +#define EPHY_TYPE_HISTORY (ephy_history_get_type ()) +#define EPHY_HISTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_HISTORY, EphyHistory)) +#define EPHY_HISTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_HISTORY, EphyHistoryClass)) +#define EPHY_IS_HISTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_HISTORY)) +#define EPHY_IS_HISTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_HISTORY)) +#define EPHY_HISTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_HISTORY, EphyHistoryClass)) + +#define EPHY_DB_ERROR_QUARK (ephy_db_error_quark) + +typedef struct _EphyHistoryClass EphyHistoryClass; +typedef struct _EphyHistory EphyHistory; +typedef struct _EphyHistoryPrivate EphyHistoryPrivate; +typedef struct _EphyHistoryPage EphyHistoryPage; +typedef struct _EphyHistoryHost EphyHistoryHost; + +struct _EphyHistory +{ + GObject parent; + + /*< private >*/ + EphyHistoryPrivate *priv; +}; + +struct _EphyHistoryClass +{ + GObjectClass parent_class; +}; + +struct _EphyHistoryPage +{ + gchar *url; + gchar *title; + gint visit_count; + gint64 host_id; + gint64 favicon_id; +}; + +struct _EphyHistoryHost +{ + gchar *url; + gchar *title; + gint64 favicon_id; +}; + +GType ephy_history_get_type (void); + +EphyHistory *ephy_history_new (void); + +gboolean ephy_history_is_enabled (EphyHistory *eh); + +gboolean ephy_history_get_page_visit_count (EphyHistory *eh, + const char *url, + gint *visit_count); + +/* Not implemented: unused in ephy-history */ +EphyHistoryPageNode *ephy_history_get_page (EphyHistory *eh, + const char *url); + +EphyHistoryPageNode *ephy_history_get_host (EphyHistory *eh, + const char *url); + +GPtrArray *ephy_history_get_hosts (EphyHistory *eh); + +GPtrArray *ephy_history_get_pages (EphyHistory *eh); + +gboolean ephy_history_set_icon (EphyHistory *eh, + const char *url, + const char *icon); + +const char *ephy_history_get_icon (EphyHistory *eh, + const char *url); + +/* + * nsIGlobalHistory2 + */ + +gboolean ephy_history_add_page (EphyHistory *eh, + const char *url, + gboolean redirect, + gboolean toplevel, + const char *ref_url); + +gboolean ephy_history_is_page_visited (EphyHistory *eh, + const char *url); + +gboolean ephy_history_set_page_title (EphyHistory *eh, + const char *url, + const char *title); +G_END_DECLS + +#endif /* EPHY_HISTORY_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 24054a4..4e10810 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -219,6 +219,7 @@ epiphany_LDADD = \ $(MOZILLA_GLUE_LIBS) \ $(DEPENDENCIES_LIBS) \ $(DBUS_LIBS) \ + $(SQLITE3_LIBS) \ $(LIBINTL) if ENABLE_PYTHON diff --git a/src/ephy-completion-model.c b/src/ephy-completion-model.c index bb2f320..da49820 100644 --- a/src/ephy-completion-model.c +++ b/src/ephy-completion-model.c @@ -35,8 +35,8 @@ static void ephy_completion_model_tree_model_init (GtkTreeModelIface *iface); struct _EphyCompletionModelPrivate { - EphyNode *history; - EphyNode *bookmarks; + GArray *history; + GArray *bookmarks; int stamp; }; @@ -48,6 +48,8 @@ enum static GObjectClass *parent_class = NULL; +#define ITER_GET_ID(iter) (GPOINTER_TO_UINT((iter)->user_data)) + GType ephy_completion_model_get_type (void) { @@ -97,20 +99,50 @@ ephy_completion_model_class_init (EphyCompletionModelClass *klass) g_type_class_add_private (object_class, sizeof (EphyCompletionModelPrivate)); } +static guint +page_ids_bsearch_compare (const void *key, const void *memberp) +{ + guint member = *(guint *)memberp; + return GPOINTER_TO_UINT(key) - member; +} + +static int +get_page_index (GArray *pages, guint id) +{ + int index; + char *ptr; + + ptr = bsearch (GUINT_TO_POINTER(id), pages->data, pages->len, + sizeof (guint), page_ids_bsearch_compare); + + if (ptr == NULL) + return -1; + + index = (ptr - pages->data) / sizeof (guint); + if (index > pages->len) + return -1; + + return index; +} + static GtkTreePath * get_path_real (EphyCompletionModel *model, - EphyNode *root, - EphyNode *child) + GArray *pages, + guint id) { GtkTreePath *retval; - int index; + int index; retval = gtk_tree_path_new (); - index = ephy_node_get_child_index (root, child); - if (root == model->priv->bookmarks) + index = get_page_index(pages, id); + + if (index == -1) + return NULL; + + if (pages == model->priv->bookmarks) { - index += ephy_node_get_n_children (model->priv->history); + index += model->priv->history->len; } gtk_tree_path_append_index (retval, index); @@ -119,30 +151,27 @@ get_path_real (EphyCompletionModel *model, } static void -node_iter_from_node (EphyCompletionModel *model, - EphyNode *root, - EphyNode *child, +iter_from_page (EphyCompletionModel *model, + GArray *pages, + guint id, GtkTreeIter *iter) { iter->stamp = model->priv->stamp; - iter->user_data = child; - iter->user_data2 = root; + iter->user_data = GUINT_TO_POINTER (id); + iter->user_data2 = pages; } -static EphyNode * -get_index_root (EphyCompletionModel *model, int *index) +static GArray * +get_pages (EphyCompletionModel *model, int *index) { - int children; - children = ephy_node_get_n_children (model->priv->history); - - if (*index >= children) + if (*index >= model->priv->history->len) { - *index = *index - children; + *index = *index - model->priv->history->len; - if (*index < ephy_node_get_n_children (model->priv->bookmarks)) + if (*index < model->priv->bookmarks->len) { - return model->priv->bookmarks; + return model->priv->bookmarks; } else { @@ -151,8 +180,9 @@ get_index_root (EphyCompletionModel *model, int *index) } else { - return model->priv->history; + return model->priv->history; } + return TRUE; } static void @@ -228,19 +258,19 @@ connect_signals (EphyCompletionModel *model, EphyNode *root) static void ephy_completion_model_init (EphyCompletionModel *model) { - EphyBookmarks *bookmarks; EphyHistory *history; model->priv = EPHY_COMPLETION_MODEL_GET_PRIVATE (model); model->priv->stamp = g_random_int (); history = EPHY_HISTORY (ephy_embed_shell_get_global_history (embed_shell)); - model->priv->history = ephy_history_get_pages (history); + model->priv->history_ids = ephy_history_get_page_ids (history); + + /* XXX: Implement signals in EphyHistory connect_signals (model, model->priv->history); + */ - bookmarks = ephy_shell_get_bookmarks (ephy_shell); - model->priv->bookmarks = ephy_bookmarks_get_bookmarks (bookmarks); - connect_signals (model, model->priv->bookmarks); + /* XXX: Bookmarks */ } EphyCompletionModel * @@ -352,6 +382,7 @@ init_keywords_col (GValue *value, EphyNode *node, int group) g_value_set_string (value, text); } + static void init_favicon_col (GValue *value, EphyNode *node, int group) { @@ -537,21 +568,21 @@ ephy_completion_model_get_iter (GtkTreeModel *tree_model, GtkTreePath *path) { EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model); - EphyNode *root, *child; - int i; + GArray *pages; + int index; + guint id; g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (model), FALSE); - g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); + g_return_val_if_fail (gtk_tree_path_get_depth (path) == 1, FALSE); - i = gtk_tree_path_get_indices (path)[0]; + index = gtk_tree_path_get_indices (path)[0]; - root = get_index_root (model, &i); - if (root == NULL) return FALSE; + pages = get_pages (model, &index); + if (pages == NULL) + return FALSE; - child = ephy_node_get_nth_child (root, i); - g_return_val_if_fail (child != NULL, FALSE); - - node_iter_from_node (model, root, child, iter); + id = g_array_index (pages, guint, index); + iter_from_page (model, pages, id, iter); return TRUE; } @@ -575,27 +606,31 @@ ephy_completion_model_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter) { EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model); - EphyNode *node, *next, *root; + GArray *pages; + guint id; + gint index, next; g_return_val_if_fail (iter != NULL, FALSE); g_return_val_if_fail (iter->user_data != NULL, FALSE); g_return_val_if_fail (iter->user_data2 != NULL, FALSE); g_return_val_if_fail (iter->stamp == model->priv->stamp, FALSE); - node = iter->user_data; - root = iter->user_data2; - - next = ephy_node_get_next_child (root, node); - - if (next == NULL && root == model->priv->history) - { - root = model->priv->bookmarks; - next = ephy_node_get_nth_child (model->priv->bookmarks, 0); - } - - if (next == NULL) return FALSE; + id = ITER_GET_ID(iter); + pages = iter->user_data2; + + index = get_page_index (pages, id); + next = index + 1; + if (next >= pages->len && pages == model->priv->history) + { + pages = model->priv->history; + index = g_array_index (pages, 0); + next = index + 1; + } + + if (next >= pages->len ) + return FALSE; - node_iter_from_node (model, root, next, iter); + iter_from_page (model, pages, next, iter); return TRUE; } @@ -606,28 +641,29 @@ ephy_completion_model_iter_children (GtkTreeModel *tree_model, GtkTreeIter *parent) { EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model); - EphyNode *root, *first_node; + GArray *pages; + guint id; if (parent != NULL) { return FALSE; } - root = model->priv->history; - first_node = ephy_node_get_nth_child (root, 0); + pages = model->priv->history; + id = g_array_index (pages, guint, 0); - if (first_node == NULL) + if (!id) { - root = model->priv->bookmarks; - first_node = ephy_node_get_nth_child (root, 0); + pages = model->priv->bookmarks; + id = g_array_index (pages, guint, 0); } - if (first_node == NULL) + if (!id) { return FALSE; } - node_iter_from_node (model, root, first_node, iter); + iter_from_page (model, root, id, iter); return TRUE; } @@ -649,8 +685,7 @@ ephy_completion_model_iter_n_children (GtkTreeModel *tree_model, if (iter == NULL) { - return ephy_node_get_n_children (model->priv->history) + - ephy_node_get_n_children (model->priv->bookmarks); + return model->priv->history->len + model->priv->bookmarks->len; } g_return_val_if_fail (model->priv->stamp == iter->stamp, -1); @@ -665,7 +700,8 @@ ephy_completion_model_iter_nth_child (GtkTreeModel *tree_model, int n) { EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model); - EphyNode *node, *root; + GArray *pages; + guint id; g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model), FALSE); @@ -674,12 +710,12 @@ ephy_completion_model_iter_nth_child (GtkTreeModel *tree_model, return FALSE; } - root = get_index_root (model, &n); - node = ephy_node_get_nth_child (root, n); - - if (node == NULL) return FALSE; + pages = get_pages (tree_model, &n); + if (pages == NULL) + return FALSE; - node_iter_from_node (model, root, node, iter); + id = g_array_index (pages, guint, n); + iter_from_page (model, pages, id, iter); return TRUE; } -- 2.11.4.GIT