diff options
Diffstat (limited to 'src/ephy-history-model.c')
-rw-r--r-- | src/ephy-history-model.c | 838 |
1 files changed, 838 insertions, 0 deletions
diff --git a/src/ephy-history-model.c b/src/ephy-history-model.c new file mode 100644 index 000000000..c4e694bcd --- /dev/null +++ b/src/ephy-history-model.c @@ -0,0 +1,838 @@ +/* + * Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org> + * + * 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 of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Id$ + */ + +#include <config.h> +#include <gtk/gtktreeview.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <libgnome/gnome-i18n.h> +#include <time.h> +#include <string.h> + +#include "ephy-node-filter.h" +#include "ephy-history-model.h" +#include "ephy-history.h" +#include "ephy-tree-model-node.h" +#include "ephy-stock-icons.h" +#include "ephy-node.h" + +static void ephy_history_model_class_init (EphyHistoryModelClass *klass); +static void ephy_history_model_init (EphyHistoryModel *model); +static void ephy_history_model_finalize (GObject *object); +static void ephy_history_model_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void ephy_history_model_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static guint ephy_history_model_get_flags (GtkTreeModel *tree_model); +static int ephy_history_model_get_n_columns (GtkTreeModel *tree_model); +static GType ephy_history_model_get_column_type (GtkTreeModel *tree_model, + int index); +static gboolean ephy_history_model_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path); +static GtkTreePath *ephy_history_model_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static void ephy_history_model_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value); +static gboolean ephy_history_model_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean ephy_history_model_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean ephy_history_model_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static int ephy_history_model_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean ephy_history_model_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n); +static gboolean ephy_history_model_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); +static void ephy_history_model_tree_model_init (GtkTreeModelIface *iface); +static void root_child_removed_cb (EphyNode *node, + EphyNode *child, + EphyHistoryModel *model); +static void root_child_added_cb (EphyNode *node, + EphyNode *child, + EphyHistoryModel *model); +static void root_child_changed_cb (EphyNode *node, + EphyNode *child, + EphyHistoryModel *model); +static inline void ephy_history_model_update_node (EphyHistoryModel *model, + EphyNode *node, + int idx); + +struct EphyHistoryModelPrivate +{ + EphyNode *root; + EphyNode *pages; + + EphyNodeFilter *filter; +}; + +enum +{ + PROP_0, + PROP_ROOT, + PROP_PAGES, + PROP_FILTER +}; + +static GObjectClass *parent_class = NULL; + +GType +ephy_history_model_get_type (void) +{ + static GType ephy_history_model_type = 0; + + if (ephy_history_model_type == 0) + { + static const GTypeInfo our_info = + { + sizeof (EphyHistoryModelClass), + NULL, + NULL, + (GClassInitFunc) ephy_history_model_class_init, + NULL, + NULL, + sizeof (EphyHistoryModel), + 0, + (GInstanceInitFunc) ephy_history_model_init + }; + + static const GInterfaceInfo tree_model_info = + { + (GInterfaceInitFunc) ephy_history_model_tree_model_init, + NULL, + NULL + }; + + ephy_history_model_type = g_type_register_static (G_TYPE_OBJECT, + "EphyHistoryModel", + &our_info, 0); + + g_type_add_interface_static (ephy_history_model_type, + GTK_TYPE_TREE_MODEL, + &tree_model_info); + } + + return ephy_history_model_type; +} + +static void +ephy_history_model_class_init (EphyHistoryModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = ephy_history_model_finalize; + + object_class->set_property = ephy_history_model_set_property; + object_class->get_property = ephy_history_model_get_property; + + g_object_class_install_property (object_class, + PROP_ROOT, + g_param_spec_object ("root", + "Root node", + "Root node", + EPHY_TYPE_NODE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_PAGES, + g_param_spec_object ("pages", + "Pages node", + "Pages node", + EPHY_TYPE_NODE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_FILTER, + g_param_spec_object ("filter", + "Filter object", + "Filter object", + EPHY_TYPE_NODE_FILTER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +ephy_history_model_init (EphyHistoryModel *model) +{ + GtkWidget *dummy; + + do + { + model->stamp = g_random_int (); + } + while (model->stamp == 0); + + model->priv = g_new0 (EphyHistoryModelPrivate, 1); + + dummy = gtk_tree_view_new (); + + gtk_widget_destroy (dummy); +} + +static void +ephy_history_model_finalize (GObject *object) +{ + EphyHistoryModel *model; + + g_return_if_fail (object != NULL); + g_return_if_fail (EPHY_IS_HISTORY_MODEL (object)); + + model = EPHY_HISTORY_MODEL (object); + + g_return_if_fail (model->priv != NULL); + + g_free (model->priv); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +filter_changed_cb (EphyNodeFilter *filter, + EphyHistoryModel *model) +{ + GPtrArray *kids; + int i; + + kids = ephy_node_get_children (model->priv->root); + + for (i = 0; i < kids->len; i++) + { + ephy_history_model_update_node (model, + g_ptr_array_index (kids, i), + i); + } + + ephy_node_thaw (model->priv->root); +} + +static void +ephy_history_model_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (object); + + switch (prop_id) + { + case PROP_ROOT: + model->priv->root = g_value_get_object (value); + g_signal_connect_object (G_OBJECT (model->priv->root), + "child_added", + G_CALLBACK (root_child_added_cb), + G_OBJECT (model), + 0); + g_signal_connect_object (G_OBJECT (model->priv->root), + "child_removed", + G_CALLBACK (root_child_removed_cb), + G_OBJECT (model), + 0); + g_signal_connect_object (G_OBJECT (model->priv->root), + "child_changed", + G_CALLBACK (root_child_changed_cb), + G_OBJECT (model), + 0); + break; + case PROP_PAGES: + model->priv->pages = g_value_get_object (value); + g_signal_connect_object (G_OBJECT (model->priv->pages), + "child_added", + G_CALLBACK (root_child_added_cb), + G_OBJECT (model), + 0); + g_signal_connect_object (G_OBJECT (model->priv->pages), + "child_removed", + G_CALLBACK (root_child_removed_cb), + G_OBJECT (model), + 0); + g_signal_connect_object (G_OBJECT (model->priv->pages), + "child_changed", + G_CALLBACK (root_child_changed_cb), + G_OBJECT (model), + 0); + break; + case PROP_FILTER: + model->priv->filter = g_value_get_object (value); + + if (model->priv->filter != NULL) + { + g_signal_connect_object (G_OBJECT (model->priv->filter), + "changed", + G_CALLBACK (filter_changed_cb), + G_OBJECT (model), + 0); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ephy_history_model_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (object); + + switch (prop_id) + { + case PROP_ROOT: + g_value_set_object (value, model->priv->root); + break; + case PROP_PAGES: + g_value_set_object (value, model->priv->root); + break; + case PROP_FILTER: + g_value_set_object (value, model->priv->filter); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +EphyHistoryModel * +ephy_history_model_new (EphyNode *root, + EphyNode *pages, + EphyNodeFilter *filter) +{ + EphyHistoryModel *model; + + model = EPHY_HISTORY_MODEL (g_object_new (EPHY_TYPE_HISTORY_MODEL, + "filter", filter, + "root", root, + "pages", pages, + NULL)); + + g_return_val_if_fail (model->priv != NULL, NULL); + + return model; +} + +static void +ephy_history_model_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = ephy_history_model_get_flags; + iface->get_n_columns = ephy_history_model_get_n_columns; + iface->get_column_type = ephy_history_model_get_column_type; + iface->get_iter = ephy_history_model_get_iter; + iface->get_path = ephy_history_model_get_path; + iface->get_value = ephy_history_model_get_value; + iface->iter_next = ephy_history_model_iter_next; + iface->iter_children = ephy_history_model_iter_children; + iface->iter_has_child = ephy_history_model_iter_has_child; + iface->iter_n_children = ephy_history_model_iter_n_children; + iface->iter_nth_child = ephy_history_model_iter_nth_child; + iface->iter_parent = ephy_history_model_iter_parent; +} + +static guint +ephy_history_model_get_flags (GtkTreeModel *tree_model) +{ + return 0; +} + +static int +ephy_history_model_get_n_columns (GtkTreeModel *tree_model) +{ + return EPHY_HISTORY_MODEL_NUM_COLUMNS; +} + +static GType +ephy_history_model_get_column_type (GtkTreeModel *tree_model, + int index) +{ + g_return_val_if_fail (EPHY_IS_HISTORY_MODEL (tree_model), G_TYPE_INVALID); + g_return_val_if_fail ((index < EPHY_HISTORY_MODEL_NUM_COLUMNS) && (index >= 0), G_TYPE_INVALID); + + switch (index) + { + case EPHY_HISTORY_MODEL_COL_TITLE: + case EPHY_HISTORY_MODEL_COL_LOCATION: + case EPHY_HISTORY_MODEL_COL_VISITS: + case EPHY_HISTORY_MODEL_COL_LAST_VISIT: + case EPHY_HISTORY_MODEL_COL_FIRST_VISIT: + return G_TYPE_STRING; + case EPHY_HISTORY_MODEL_COL_VISIBLE: + return G_TYPE_BOOLEAN; + default: + g_assert_not_reached (); + return G_TYPE_INVALID; + } +} + +static gboolean +ephy_history_model_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (tree_model); + gint *indices; + gint depth; + EphyNode *host; + + g_return_val_if_fail (EPHY_IS_HISTORY_MODEL (model), FALSE); + + indices = gtk_tree_path_get_indices (path); + depth = gtk_tree_path_get_depth (path); + + g_return_val_if_fail (depth > 0, FALSE); + + iter->stamp = model->stamp; + host = ephy_node_get_nth_child (model->priv->root, indices [0]); + + if (depth == 2 && host != NULL) + { + iter->user_data = ephy_node_get_nth_child (host, indices [1]); + } + else + { + iter->user_data = host; + } + + if (iter->user_data == NULL) + { + iter->stamp = 0; + return FALSE; + } + + return TRUE; +} + +static EphyNode * +ensure_iter (EphyHistoryModel *model, GtkTreeIter *parent) +{ + EphyNode *node; + + if (parent) + { + node = EPHY_NODE (parent->user_data); + } + else + { + node = model->priv->root; + } + + return node; +} + +static EphyNode * +get_parent_node (EphyHistoryModel *model, EphyNode *node) +{ + int host_id; + + host_id = ephy_node_get_property_int (node, EPHY_NODE_PAGE_PROP_HOST_ID); + + if (host_id < 0) + { + return model->priv->root; + } + else + { + EphyNode *host; + host = ephy_node_get_from_id (host_id); + return host; + } +} + +static inline GtkTreePath * +get_one_level_path_real (EphyHistoryModel *model, + EphyNode *node) +{ + GtkTreePath *retval; + EphyNode *my_parent; + + retval = gtk_tree_path_new (); + + my_parent = get_parent_node (model, node); + + gtk_tree_path_append_index (retval, ephy_node_get_child_index (my_parent, node)); + + return retval; +} + +static inline GtkTreePath * +get_path_real (EphyHistoryModel *model, + EphyNode *page) +{ + GtkTreePath *retval; + EphyNode *host; + + retval = gtk_tree_path_new (); + host = get_parent_node (model, page); + + gtk_tree_path_append_index (retval, ephy_node_get_child_index (model->priv->root, host)); + gtk_tree_path_append_index (retval, ephy_node_get_child_index (host, page)); + + return retval; +} + +static GtkTreePath * +ephy_history_model_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (tree_model); + EphyNode *node; + + g_return_val_if_fail (EPHY_IS_HISTORY_MODEL (tree_model), NULL); + g_return_val_if_fail (iter != NULL, NULL); + g_return_val_if_fail (iter->user_data != NULL, NULL); + g_return_val_if_fail (iter->stamp == model->stamp, NULL); + + node = EPHY_NODE (iter->user_data); + + if (node == model->priv->root) + return gtk_tree_path_new (); + + return get_one_level_path_real (model, node); +} + +static void +get_property_as_date (EphyNode *node, + int id, + GValue *value) +{ + GTime time; + char s[50]; + GDate *date; + + time = ephy_node_get_property_int (node, id); + date = g_date_new (); + g_date_set_time (date, time); + g_date_strftime (s, 50, "%c", date); + + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, s); + + g_date_free (date); +} + +static void +ephy_history_model_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (tree_model); + EphyNode *node; + + g_return_if_fail (EPHY_IS_HISTORY_MODEL (tree_model)); + g_return_if_fail (iter != NULL); + g_return_if_fail (iter->stamp == model->stamp); + g_return_if_fail (EPHY_IS_NODE (iter->user_data)); + g_return_if_fail (column < EPHY_HISTORY_MODEL_NUM_COLUMNS); + + node = EPHY_NODE (iter->user_data); + + if (ephy_node_get_property_int (node, EPHY_NODE_PAGE_PROP_HOST_ID) < 0 && + (column == EPHY_HISTORY_MODEL_COL_LOCATION || + column == EPHY_HISTORY_MODEL_COL_FIRST_VISIT || + column == EPHY_HISTORY_MODEL_COL_LAST_VISIT || + column == EPHY_HISTORY_MODEL_COL_VISITS)) + { + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, ""); + return; + } + + switch (column) + { + case EPHY_HISTORY_MODEL_COL_TITLE: + ephy_node_get_property (node, + EPHY_NODE_PAGE_PROP_TITLE, + value); + break; + case EPHY_HISTORY_MODEL_COL_LOCATION: + ephy_node_get_property (node, + EPHY_NODE_PAGE_PROP_LOCATION, + value); + break; + case EPHY_HISTORY_MODEL_COL_VISITS: + ephy_node_get_property (node, + EPHY_NODE_PAGE_PROP_VISITS, + value); + break; + case EPHY_HISTORY_MODEL_COL_FIRST_VISIT: + get_property_as_date (node, + EPHY_NODE_PAGE_PROP_FIRST_VISIT, + value); + break; + case EPHY_HISTORY_MODEL_COL_LAST_VISIT: + get_property_as_date (node, + EPHY_NODE_PAGE_PROP_LAST_VISIT, + value); + break; + case EPHY_HISTORY_MODEL_COL_VISIBLE: + g_value_init (value, G_TYPE_BOOLEAN); + + if (model->priv->filter != NULL) + { + g_value_set_boolean (value, + ephy_node_filter_evaluate (model->priv->filter, node)); + } + else + { + g_value_set_boolean (value, TRUE); + } + break; + default: + g_assert_not_reached (); + break; + } +} + +static gboolean +ephy_history_model_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (tree_model); + EphyNode *node; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (iter->user_data != NULL, FALSE); + g_return_val_if_fail (iter->stamp == EPHY_HISTORY_MODEL (tree_model)->stamp, FALSE); + + node = EPHY_NODE (iter->user_data); + + iter->user_data = ephy_node_get_next_child + (get_parent_node (model, node), node); + + return (iter->user_data != NULL); +} + +static gboolean +ephy_history_model_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (tree_model); + EphyNode *node; + + node = ensure_iter (model, parent); + + iter->user_data = ephy_node_get_nth_child (node, 0); + iter->stamp = model->stamp; + + return (iter->user_data != NULL); +} + +static gboolean +ephy_history_model_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (tree_model); + EphyNode *node; + int host_id; + + node = EPHY_NODE (iter->user_data); + host_id = ephy_node_get_property_int (node, EPHY_NODE_PAGE_PROP_HOST_ID); + if (host_id < 0) + { + return ephy_node_has_child (model->priv->root, node); + } + else + { + return FALSE; + } +} + +static int +ephy_history_model_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (tree_model); + EphyNode *node; + + g_return_val_if_fail (iter == NULL || iter->user_data != NULL, FALSE); + + node = ensure_iter (model, iter); + + return ephy_node_get_n_children (node); +} + +static gboolean +ephy_history_model_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (tree_model); + EphyNode *node; + + node = ensure_iter (model, parent); + + iter->user_data = ephy_node_get_nth_child (node, n); + iter->stamp = model->stamp; + + return (iter->user_data != NULL); +} + +static gboolean +ephy_history_model_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + EphyHistoryModel *model = EPHY_HISTORY_MODEL (tree_model); + EphyNode *parent, *node; + + node = EPHY_NODE (iter->user_data); + parent = get_parent_node (model, node); + + if (parent != model->priv->root) + { + iter->user_data = parent; + iter->stamp = model->stamp; + return TRUE; + } + else + { + return FALSE; + } +} + +EphyNode * +ephy_history_model_node_from_iter (EphyHistoryModel *model, + GtkTreeIter *iter) +{ + return EPHY_NODE (iter->user_data); +} + +void +ephy_history_model_iter_from_node (EphyHistoryModel *model, + EphyNode *node, + GtkTreeIter *iter) +{ + iter->stamp = model->stamp; + iter->user_data = node; +} + +static inline void +ephy_history_model_update_node (EphyHistoryModel *model, + EphyNode *node, + int idx) +{ + GtkTreePath *path; + GtkTreeIter iter; + + ephy_history_model_iter_from_node (model, node, &iter); + + if (idx >= 0) + { + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, idx); + } + else + { + path = get_one_level_path_real (model, node); + } + + gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter); + gtk_tree_path_free (path); +} + +static void +root_child_removed_cb (EphyNode *node, + EphyNode *child, + EphyHistoryModel *model) +{ + GtkTreePath *path; + + if (node == model->priv->root) + { + path = get_one_level_path_real (model, child); + } + else + { + path = get_path_real (model, child); + } + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + gtk_tree_path_free (path); +} + +static void +root_child_added_cb (EphyNode *node, + EphyNode *child, + EphyHistoryModel *model) +{ + GtkTreePath *path; + GtkTreeIter iter; + + ephy_history_model_iter_from_node (model, child, &iter); + + if (node == model->priv->root) + { + path = get_one_level_path_real (model, child); + } + else + { + path = get_path_real (model, child); + } + + gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); + gtk_tree_path_free (path); +} + +static void +root_child_changed_cb (EphyNode *node, + EphyNode *child, + EphyHistoryModel *model) +{ + ephy_history_model_update_node (model, child, -1); +} + +GType +ephy_history_model_column_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) + { + static const GEnumValue values[] = + { + { EPHY_HISTORY_MODEL_COL_TITLE, "EPHY_HISTORY_MODEL_COL_TITLE", "title" }, + { EPHY_HISTORY_MODEL_COL_LOCATION, "EPHY_HISTORY_MODEL_COL_LOCATION", "location" }, + { EPHY_HISTORY_MODEL_COL_VISITS, "EPHY_HISTORY_MODEL_COL_VISITS", "visits" }, + { EPHY_HISTORY_MODEL_COL_FIRST_VISIT, "EPHY_HISTORY_MODEL_COL_FIRST_VISIT", "last_visit" }, + { EPHY_HISTORY_MODEL_COL_LAST_VISIT, "EPHY_HISTORY_MODEL_COL_LAST_VISIT", "last_visit" }, + { EPHY_HISTORY_MODEL_COL_VISIBLE, "EPHY_HISTORY_MODEL_COL_FIRST_VISIT", "first_visit" }, + + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("EphyHistoryModelColumn", values); + } + + return etype; +} + |