diff options
Diffstat (limited to 'src/bookmarks/ephy-topics-palette.c')
-rw-r--r-- | src/bookmarks/ephy-topics-palette.c | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/src/bookmarks/ephy-topics-palette.c b/src/bookmarks/ephy-topics-palette.c new file mode 100644 index 000000000..ad2731dec --- /dev/null +++ b/src/bookmarks/ephy-topics-palette.c @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2002-2004 Marco Pesenti Gritti <mpeseng@tin.it> + * Copyright (C) 2005 Peter Harvey <pah06@uow.edu.au> + * + * 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. + */ + +#include "config.h" + +#include "ephy-topics-palette.h" +#include "ephy-nodes-cover.h" +#include "ephy-node-common.h" +#include "ephy-bookmarks.h" +#include "ephy-debug.h" + +#include <glib/gi18n.h> +#include <gtk/gtktreeselection.h> +#include <gdk/gdkkeysyms.h> + +static void ephy_topics_palette_class_init (EphyTopicsPaletteClass *klass); +static void ephy_topics_palette_init (EphyTopicsPalette *editor); + +#define EPHY_TOPICS_PALETTE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_TOPICS_PALETTE, EphyTopicsPalettePrivate)) + +struct _EphyTopicsPalettePrivate +{ + EphyBookmarks *bookmarks; + EphyNode *bookmark; + GtkListStore *store; + int mode; +}; + +enum +{ + PROP_0, + PROP_BOOKMARKS, + PROP_BOOKMARK, + PROP_MODE +}; + +enum +{ + COLUMN_TITLE, + COLUMN_NODE, + COLUMN_WEIGHT, + COLUMN_SELECTED, + COLUMN_SEPARATOR, + COLUMN_SELECTABLE, + COLUMNS +}; + +enum +{ + MODE_ALL, + MODE_TOPLEVEL, + MODE_COMMUNITY +}; + +static GObjectClass *parent_class = NULL; + +GType +ephy_topics_palette_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GTypeInfo our_info = + { + sizeof (EphyTopicsPaletteClass), + NULL, + NULL, + (GClassInitFunc) ephy_topics_palette_class_init, + NULL, + NULL, + sizeof (EphyTopicsPalette), + 0, + (GInstanceInitFunc) ephy_topics_palette_init + }; + + type = g_type_register_static (GTK_TYPE_TREE_VIEW, + "EphyTopicsPalette", + &our_info, 0); + } + + return type; +} + +static void +append_topics (EphyTopicsPalette *palette, + GtkTreeIter *iter, + gboolean *valid, + gboolean *first, + const char *label, + const char *empty, + GPtrArray *topics) +{ + EphyNode *node; + const char *title; + char *noted; + gint i; + + if (!*first) + { + if (!*valid) gtk_list_store_append (palette->priv->store, iter); + gtk_list_store_set (palette->priv->store, iter, COLUMN_TITLE, NULL, + COLUMN_NODE, NULL, COLUMN_SEPARATOR, TRUE, -1); + *valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (palette->priv->store), iter); + } + + if (label != NULL) + { + if (!*valid) gtk_list_store_append (palette->priv->store, iter); + gtk_list_store_set (palette->priv->store, iter, COLUMN_TITLE, label, + COLUMN_NODE, NULL, COLUMN_SELECTED, FALSE, + COLUMN_WEIGHT, PANGO_WEIGHT_BOLD, + COLUMN_SEPARATOR, FALSE, COLUMN_SELECTABLE, FALSE, -1); + *valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (palette->priv->store), iter); + *first = FALSE; + } + + if (!topics || topics->len == 0) + { + if (empty != NULL) + { + if (!*valid) gtk_list_store_append (palette->priv->store, iter); + gtk_list_store_set (palette->priv->store, iter, COLUMN_TITLE, empty, + COLUMN_NODE, NULL, COLUMN_SELECTED, FALSE, + COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL, + COLUMN_SEPARATOR, FALSE, COLUMN_SELECTABLE, FALSE, -1); + *valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (palette->priv->store), iter); + *first = FALSE; + } + } + else + { + for (i = 0; i < topics->len ; i++) + { + node = g_ptr_array_index (topics, i); + title = ephy_node_get_property_string (node, EPHY_NODE_KEYWORD_PROP_NAME); + noted = NULL; + if (ephy_node_get_children(node)->len == 0) + { + noted = g_strdup_printf ("%s (unused)", title); + } + + if (!*valid) gtk_list_store_append (palette->priv->store, iter); + gtk_list_store_set (palette->priv->store, iter, COLUMN_TITLE, noted ? noted : title, + COLUMN_NODE, node, COLUMN_SELECTED, + ephy_node_has_child (node, palette->priv->bookmark), + COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL, + COLUMN_SEPARATOR, FALSE, COLUMN_SELECTABLE, TRUE, -1); + *valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (palette->priv->store), iter); + *first = FALSE; + g_free (noted); + } + } +} + +static void +update_list (EphyTopicsPalette *palette) +{ + GPtrArray *children, *bookmarks, *topics; + EphyNode *node; + GtkTreeIter iter; + gint i, priority; + gboolean valid, first; + + gtk_widget_queue_draw (GTK_WIDGET (palette)); + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (palette->priv->store), &iter); + first = TRUE; + + if (palette->priv->mode == MODE_ALL) + { + /* Allocate and fill the suggestions array. */ + node = ephy_bookmarks_get_keywords (palette->priv->bookmarks); + children = ephy_node_get_children (node); + topics = g_ptr_array_sized_new (children->len); + for (i = 0; i < children->len; i++) + { + node = g_ptr_array_index (children, i); + + priority = ephy_node_get_property_int (node, EPHY_NODE_KEYWORD_PROP_PRIORITY); + if (priority != EPHY_NODE_NORMAL_PRIORITY) + continue; + + g_ptr_array_add (topics, node); + } + + g_ptr_array_sort (topics, ephy_bookmarks_compare_topic_pointers); + append_topics (palette, &iter, &valid, &first, NULL, _("No topics"), topics); + g_ptr_array_free (topics, TRUE); + } + else if (palette->priv->mode == MODE_TOPLEVEL) + { + GPtrArray *suggested, *selected; + + /* Allocate and fill the bookmarks array. */ + node = ephy_bookmarks_get_bookmarks (palette->priv->bookmarks); + children = ephy_node_get_children (node); + bookmarks = g_ptr_array_sized_new (children->len); + for (i = 0; i < children->len; i++) + { + g_ptr_array_add(bookmarks, g_ptr_array_index (children, i)); + } + + /* Allocate and fill the topics array. */ + node = ephy_bookmarks_get_keywords (palette->priv->bookmarks); + children = ephy_node_get_children (node); + topics = g_ptr_array_sized_new (children->len); + suggested = g_ptr_array_sized_new (children->len); + selected = g_ptr_array_sized_new (children->len); + for (i = 0; i < children->len; i++) + { + node = g_ptr_array_index (children, i); + + priority = ephy_node_get_property_int (node, EPHY_NODE_KEYWORD_PROP_PRIORITY); + if (priority != EPHY_NODE_NORMAL_PRIORITY) + continue; + + /* We'll consider only bookmarks covered by the same topics as our bookmark. */ + if (ephy_node_has_child (node, palette->priv->bookmark)) + { + ephy_nodes_remove_not_covered (node, bookmarks); + g_ptr_array_add (selected, node); + } + + /* We'll onsider only topics that are not already selected for our bookmark. */ + else + { + g_ptr_array_add (topics, node); + } + } + + /* Get the minimum cover of topics for the bookmarks. */ + suggested = ephy_nodes_get_covering (topics, bookmarks, suggested, 0, 0); + + for (i = 0; i < suggested->len; i++) + { + g_ptr_array_remove_fast (topics, g_ptr_array_index (suggested, i)); + } + + /* Add any topics which cover the bookmarks completely in their own right, or + have no bookmarks currently associated with it. */ + for (i = 0; i < topics->len ; i++) + { + node = g_ptr_array_index (topics, i); + if (!ephy_node_has_child (node, palette->priv->bookmark) && + ephy_nodes_covered (node, bookmarks)) + { + g_ptr_array_add (suggested, node); + g_ptr_array_remove_index_fast (topics, i); + i--; + } + } + + + g_ptr_array_sort (selected, ephy_bookmarks_compare_topic_pointers); + g_ptr_array_sort (suggested, ephy_bookmarks_compare_topic_pointers); + g_ptr_array_sort (topics, ephy_bookmarks_compare_topic_pointers); + append_topics (palette, &iter, &valid, &first, _("Selection"), _("No selected topics"), selected); + append_topics (palette, &iter, &valid, &first, _("Subtopics"), _("No more subtopics"), suggested); + append_topics (palette, &iter, &valid, &first, _("Other"), _("No other topics"), topics); + g_ptr_array_free (selected, TRUE); + g_ptr_array_free (suggested, TRUE); + g_ptr_array_free (bookmarks, TRUE); + g_ptr_array_free (topics, TRUE); + } + + while (valid) + { + valid = gtk_list_store_remove (palette->priv->store, &iter); + } + +} + +static gboolean +update_list_idle (EphyTopicsPalette *palette) +{ + update_list (palette); + return FALSE; +} + +static void +tree_changed_cb (EphyBookmarks *bookmarks, + EphyTopicsPalette *palette) +{ + g_idle_add ((GSourceFunc) update_list_idle, palette); +} + + +static void +child_changed_cb (EphyNode *node, + EphyNode *child, + guint property_id, + EphyTopicsPalette *palette) +{ + g_idle_add ((GSourceFunc) update_list_idle, palette); +} + + +static void +ephy_topics_palette_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EphyTopicsPalette *palette = EPHY_TOPICS_PALETTE (object); + + switch (prop_id) + { + case PROP_BOOKMARKS: + palette->priv->bookmarks = g_value_get_object (value); + g_signal_connect_object (palette->priv->bookmarks, "tree-changed", + G_CALLBACK (tree_changed_cb), palette, + G_CONNECT_AFTER); + ephy_node_signal_connect_object + (ephy_bookmarks_get_keywords (palette->priv->bookmarks), + EPHY_NODE_CHILD_CHANGED, (EphyNodeCallback) child_changed_cb, object); + break; + case PROP_BOOKMARK: + palette->priv->bookmark = g_value_get_pointer (value); + break; + case PROP_MODE: + palette->priv->mode = g_value_get_int (value); + update_list_idle (palette); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +cell_edited (GtkCellRendererText *renderer, + const char *path_str, + const char *new_text, + EphyTopicsPalette *palette) +{ + if (*new_text != 0) + { + EphyNode *node; + node = ephy_bookmarks_add_keyword (palette->priv->bookmarks, new_text); + ephy_bookmarks_set_keyword (palette->priv->bookmarks, node, + palette->priv->bookmark); + } + else + { + update_list (palette); + } +} + +static void +toggled (GtkCellRendererToggle *cell_renderer, + gchar *path, + EphyTopicsPalette *palette) +{ + EphyNode *topic; + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (palette)); + + if (gtk_tree_model_get_iter_from_string (model, &iter, path)) + { + gtk_tree_model_get (model, &iter, COLUMN_NODE, &topic, -1); + if (topic == NULL) + { + char *title; + gtk_tree_model_get (model, &iter, COLUMN_TITLE, &title, -1); + g_return_if_fail (title != NULL && *title != 0); + topic = ephy_bookmarks_add_keyword (palette->priv->bookmarks, title); + g_free (title); + } + + if (ephy_node_has_child (topic, palette->priv->bookmark)) + { + ephy_bookmarks_unset_keyword (palette->priv->bookmarks, + topic, + palette->priv->bookmark); + } + else + { + ephy_bookmarks_set_keyword (palette->priv->bookmarks, + topic, + palette->priv->bookmark); + } + } +} + +static gboolean +is_separator (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean separator; + gtk_tree_model_get (model, iter, COLUMN_SEPARATOR, &separator, -1); + return separator; +} + +static GObject * +ephy_topics_palette_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) + +{ + GObject *object; + EphyTopicsPalette *palette; + EphyTopicsPalettePrivate *priv; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + object = parent_class->constructor (type, n_construct_properties, + construct_params); + palette = EPHY_TOPICS_PALETTE (object); + priv = EPHY_TOPICS_PALETTE_GET_PRIVATE (object); + + priv->store = gtk_list_store_new (COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); + gtk_tree_view_set_model (GTK_TREE_VIEW (object), GTK_TREE_MODEL (priv->store)); + + column = gtk_tree_view_column_new (); + + renderer = gtk_cell_renderer_toggle_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_add_attribute (column, renderer, "active", COLUMN_SELECTED); + gtk_tree_view_column_add_attribute (column, renderer, "visible", COLUMN_SELECTABLE); + g_signal_connect (renderer, "toggled", G_CALLBACK (toggled), palette); + + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_add_attribute (column, renderer, "text", COLUMN_TITLE); + gtk_tree_view_column_add_attribute (column, renderer, "weight", COLUMN_WEIGHT); + g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited), palette); + + gtk_tree_view_append_column (GTK_TREE_VIEW (object), column); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (object), FALSE); + gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (object), is_separator, NULL, NULL); + gtk_tree_view_set_enable_search (GTK_TREE_VIEW (object), TRUE); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (object), COLUMN_TITLE); + gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (object)), GTK_SELECTION_NONE); + + update_list (palette); + + return object; +} + +static void +ephy_topics_palette_init (EphyTopicsPalette *palette) +{ + palette->priv = EPHY_TOPICS_PALETTE_GET_PRIVATE (palette); +} + +static void +ephy_topics_palette_finalize (GObject *object) +{ + EphyTopicsPalette *palette = EPHY_TOPICS_PALETTE (object); + + parent_class->finalize (object); +} + +GtkWidget * +ephy_topics_palette_new (EphyBookmarks *bookmarks, + EphyNode *bookmark) +{ + EphyTopicsPalette *palette; + + g_assert (bookmarks != NULL); + + palette = EPHY_TOPICS_PALETTE (g_object_new + (EPHY_TYPE_TOPICS_PALETTE, + "bookmarks", bookmarks, + "bookmark", bookmark, + NULL)); + + return GTK_WIDGET (palette); +} + +static void +ephy_topics_palette_class_init (EphyTopicsPaletteClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->set_property = ephy_topics_palette_set_property; + object_class->constructor = ephy_topics_palette_constructor; + object_class->finalize = ephy_topics_palette_finalize; + + g_object_class_install_property (object_class, + PROP_BOOKMARKS, + g_param_spec_object ("bookmarks", + "Bookmarks set", + "Bookmarks set", + EPHY_TYPE_BOOKMARKS, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_BOOKMARK, + g_param_spec_pointer ("bookmark", + "Bookmark", + "Bookmark", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_MODE, + g_param_spec_int ("mode", + "Mode", + "Mode", + 0, 2, 1, G_PARAM_WRITABLE)); + + g_type_class_add_private (object_class, sizeof(EphyTopicsPalettePrivate)); +} |