aboutsummaryrefslogtreecommitdiffstats
path: root/src/bookmarks/ephy-topics-entry.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bookmarks/ephy-topics-entry.c')
-rw-r--r--src/bookmarks/ephy-topics-entry.c520
1 files changed, 520 insertions, 0 deletions
diff --git a/src/bookmarks/ephy-topics-entry.c b/src/bookmarks/ephy-topics-entry.c
new file mode 100644
index 000000000..8c3af0007
--- /dev/null
+++ b/src/bookmarks/ephy-topics-entry.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2002-2004 Marco Pesenti Gritti <mpeseng@tin.it>
+ * Copyright (C) 2005, 2006 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-entry.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 <gtk/gtkentrycompletion.h>
+#include <string.h>
+
+static void ephy_topics_entry_class_init (EphyTopicsEntryClass *klass);
+static void ephy_topics_entry_init (EphyTopicsEntry *editor);
+
+#define EPHY_TOPICS_ENTRY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_TOPICS_ENTRY, EphyTopicsEntryPrivate))
+
+struct _EphyTopicsEntryPrivate
+{
+ EphyBookmarks *bookmarks;
+ EphyNode *bookmark;
+ GtkListStore *store;
+ GtkEntryCompletion *completion;
+ gboolean update_keywords;
+ char *action;
+};
+
+enum
+{
+ PROP_0,
+ PROP_BOOKMARKS,
+ PROP_BOOKMARK
+};
+
+static GObjectClass *parent_class = NULL;
+
+GType
+ephy_topics_entry_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0))
+ {
+ static const GTypeInfo our_info =
+ {
+ sizeof (EphyTopicsEntryClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) ephy_topics_entry_class_init,
+ NULL,
+ NULL,
+ sizeof (EphyTopicsEntry),
+ 0,
+ (GInstanceInitFunc) ephy_topics_entry_init
+ };
+
+ type = g_type_register_static (GTK_TYPE_ENTRY,
+ "EphyTopicsEntry",
+ &our_info, 0);
+ }
+
+ return type;
+}
+
+static void
+update_widget (EphyTopicsEntry *entry)
+{
+ EphyNode *node;
+ GPtrArray *children, *topics;
+ GtkTreeIter iter;
+ gint i, priority;
+ const char *title;
+ char *tmp1, *tmp2;
+ gboolean update_text;
+
+ entry->priv->update_keywords = FALSE;
+
+ update_text = !GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (entry));
+ gtk_entry_set_completion (GTK_ENTRY (entry), NULL);
+ gtk_list_store_clear (entry->priv->store);
+ if (update_text) gtk_entry_set_text (GTK_ENTRY (entry), "");
+
+ node = ephy_bookmarks_get_keywords (entry->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);
+
+ 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);
+
+ if (ephy_node_has_child (node, entry->priv->bookmark))
+ {
+ if (update_text)
+ {
+ gtk_entry_append_text (GTK_ENTRY (entry), title);
+ gtk_entry_append_text (GTK_ENTRY (entry), "; ");
+ }
+ }
+ else
+ {
+ tmp1 = g_utf8_casefold (title, -1);
+ tmp2 = g_utf8_normalize (tmp1, -1, G_NORMALIZE_DEFAULT);
+ gtk_list_store_append (entry->priv->store, &iter);
+ gtk_list_store_set (entry->priv->store, &iter, 0, title, 1, tmp2, -1);
+ g_free (tmp2);
+ g_free (tmp1);
+ }
+ }
+
+ g_ptr_array_free (topics, TRUE);
+
+ gtk_entry_set_completion (GTK_ENTRY (entry), entry->priv->completion);
+ if (update_text) gtk_editable_set_position (GTK_EDITABLE (entry), -1);
+
+ entry->priv->update_keywords = TRUE;
+}
+
+static void
+update_action (EphyTopicsEntry *entry)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ const char *text = gtk_entry_get_text (GTK_ENTRY (entry));
+ char *key;
+ gint start, end;
+
+ if(entry->priv->action)
+ {
+ gtk_entry_completion_delete_action (entry->priv->completion, 0);
+ g_free (entry->priv->action);
+ entry->priv->action = 0;
+ }
+
+ /* Find the start and end locations */
+ start = gtk_editable_get_position (editable);
+ while (start > 0 && text[start-1] != ';')
+ {
+ start--;
+ }
+ if(start > 0 && text[start-1] == ';' && text[start] == ' ')
+ {
+ start++;
+ }
+ end = start;
+ while (text[end] && text[end] != ';')
+ {
+ end++;
+ }
+
+ /* If no text to work with, exit */
+ key = g_strndup (text+start, end-start);
+ g_strstrip (key);
+ if (*key != 0 && ephy_bookmarks_find_keyword (entry->priv->bookmarks, key, FALSE) == NULL)
+ {
+ entry->priv->action = key;
+ key = g_strdup_printf (_("Create topic ā€œ%sā€"), key);
+ gtk_entry_completion_insert_action_text (entry->priv->completion, 0, key);
+ }
+
+ g_free (key);
+}
+
+static void
+update_keywords (EphyTopicsEntry *entry)
+{
+ EphyNode *node;
+ GPtrArray *children;
+ const char *text;
+ char **split;
+ char *title, *tmp;
+ gint i, j, priority;
+
+ if(!entry->priv->update_keywords) return;
+
+ /* Get the list of strings input by the user */
+ text = gtk_entry_get_text (GTK_ENTRY (entry));
+ split = g_strsplit (text, ";", 0);
+ for (i=0; split[i]; i++)
+ {
+ g_strstrip (split[i]);
+
+ tmp = g_utf8_casefold (split[i], -1);
+ g_free (split[i]);
+
+ split[i] = g_utf8_normalize (tmp, -1, G_NORMALIZE_DEFAULT);
+ g_free (tmp);
+ }
+
+ /* Test each keyword and set/unset as appropriate */
+ node = ephy_bookmarks_get_keywords (entry->priv->bookmarks);
+ children = ephy_node_get_children (node);
+ 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;
+
+ text = ephy_node_get_property_string (node, EPHY_NODE_KEYWORD_PROP_NAME);
+ tmp = g_utf8_casefold (text, -1);
+ title = g_utf8_normalize (tmp, -1, G_NORMALIZE_DEFAULT);
+ g_free (tmp);
+
+ for (j=0; split[j]; j++)
+ if (strcmp (title, split[j]) == 0)
+ break;
+
+ if (split[j])
+ {
+ split[j][0] = 0;
+ ephy_bookmarks_set_keyword (entry->priv->bookmarks, node,
+ entry->priv->bookmark);
+ }
+ else
+ {
+ ephy_bookmarks_unset_keyword (entry->priv->bookmarks, node,
+ entry->priv->bookmark);
+ }
+
+ g_free (title);
+ }
+
+ g_strfreev (split);
+
+ update_action (entry);
+}
+
+static void
+insert_text (EphyTopicsEntry *entry,
+ const char *title)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ const char *text = gtk_entry_get_text (GTK_ENTRY (entry));
+ char *key;
+ gint start, end;
+
+ /* Find the start and end locations */
+ start = gtk_editable_get_position (editable);
+ while (start > 0 && text[start-1] != ';')
+ {
+ start--;
+ }
+ if(start > 0 && text[start-1] == ';' && text[start] == ' ')
+ {
+ start++;
+ }
+ end = start;
+ while (text[end] && text[end] != ';')
+ {
+ end++;
+ }
+ end = end;
+ if (text[end] == ';')
+ {
+ end++;
+ if (text[end] == ' ')
+ {
+ end++;
+ }
+ }
+
+ /* If we were provided with nothing, then we're meant to find a topic
+ * or create one from whatever has been entered */
+ if (title == NULL)
+ {
+ /* If no text to work with, exit */
+ key = g_strndup (text+start, end-start);
+ g_strstrip (key);
+ if (*key == 0)
+ {
+ g_free (key);
+ return;
+ }
+
+ }
+
+ /* Replace the text in the current position with the title */
+ gtk_editable_delete_text (editable, start, end);
+ gtk_editable_insert_text (editable, title, strlen(title), &start);
+ gtk_editable_insert_text (editable, "; ", 2, &start);
+ gtk_editable_set_position (editable, start);
+ update_widget(entry);
+}
+
+static void
+action_cb (GtkEntryCompletion *completion,
+ gint index,
+ gpointer user_data)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (gtk_entry_completion_get_entry (completion));
+ char *action = g_strdup(entry->priv->action);
+
+ if (ephy_bookmarks_find_keyword (entry->priv->bookmarks, action, FALSE) == NULL)
+ {
+ ephy_bookmarks_add_keyword (entry->priv->bookmarks, action);
+ }
+
+ insert_text (entry, action);
+ g_free (action);
+}
+
+static gboolean
+match_selected_cb (GtkEntryCompletion *completion,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (gtk_entry_completion_get_entry (completion));
+ char *title;
+
+ gtk_tree_model_get (model, iter, 0, &title, -1);
+ insert_text (entry, title);
+ g_free (title);
+
+ return TRUE;
+}
+
+static void
+tree_changed_cb (EphyBookmarks *bookmarks,
+ EphyTopicsEntry *entry)
+{
+ update_widget(entry);
+}
+
+static gboolean
+focus_out_cb (GtkEditable *editable,
+ GdkEventFocus *event,
+ gpointer user_data)
+{
+ update_widget (EPHY_TOPICS_ENTRY (editable));
+ return FALSE;
+}
+
+static gboolean
+match_func (GtkEntryCompletion *completion,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gboolean result;
+ const char *real_key;
+ char *text;
+
+ real_key = g_strrstr (key, ";");
+ if (real_key) real_key++;
+ else real_key = key;
+ while (*real_key == ' ') real_key++;
+
+ gtk_tree_model_get (gtk_entry_completion_get_model (completion), iter, 1, &text, -1);
+ if (text == NULL) return FALSE;
+ result = g_str_has_prefix (text, real_key);
+ g_free (text);
+
+ return result;
+}
+
+static void
+ephy_topics_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (object);
+
+ switch (prop_id)
+ {
+ case PROP_BOOKMARKS:
+ entry->priv->bookmarks = g_value_get_object (value);
+ g_signal_connect_object (entry->priv->bookmarks, "tree-changed",
+ G_CALLBACK (tree_changed_cb), entry,
+ G_CONNECT_AFTER);
+ break;
+ case PROP_BOOKMARK:
+ entry->priv->bookmark = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ephy_topics_entry_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ EphyTopicsEntry *entry;
+ EphyTopicsEntryPrivate *priv;
+
+ object = parent_class->constructor (type, n_construct_properties,
+ construct_params);
+ entry = EPHY_TOPICS_ENTRY (object);
+ priv = EPHY_TOPICS_ENTRY_GET_PRIVATE (object);
+
+ priv->store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+ priv->completion = gtk_entry_completion_new ();
+ priv->update_keywords = TRUE;
+
+ gtk_entry_completion_set_model (priv->completion, GTK_TREE_MODEL (priv->store));
+ gtk_entry_completion_set_text_column (priv->completion, 0);
+ gtk_entry_completion_set_popup_completion (priv->completion, TRUE);
+ gtk_entry_completion_set_popup_single_match (priv->completion, TRUE);
+
+ gtk_entry_completion_set_match_func (priv->completion, match_func, NULL, NULL);
+
+ g_signal_connect (priv->completion, "match-selected",
+ G_CALLBACK (match_selected_cb), NULL);
+ g_signal_connect (object, "focus-out-event",
+ G_CALLBACK (focus_out_cb), NULL);
+ g_signal_connect (object, "changed",
+ G_CALLBACK (update_keywords), NULL);
+ g_signal_connect (G_OBJECT (priv->completion), "action-activated",
+ G_CALLBACK (action_cb), NULL);
+
+ update_widget (entry);
+
+ return object;
+}
+
+static void
+ephy_topics_entry_init (EphyTopicsEntry *entry)
+{
+ entry->priv = EPHY_TOPICS_ENTRY_GET_PRIVATE (entry);
+
+}
+
+static void
+ephy_topics_entry_finalize (GObject *object)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (object);
+
+ g_free (entry->priv->action);
+
+ parent_class->finalize (object);
+}
+
+GtkWidget *
+ephy_topics_entry_new (EphyBookmarks *bookmarks,
+ EphyNode *bookmark)
+{
+ EphyTopicsEntry *entry;
+
+ g_assert (bookmarks != NULL);
+
+ entry = EPHY_TOPICS_ENTRY (g_object_new
+ (EPHY_TYPE_TOPICS_ENTRY,
+ "bookmarks", bookmarks,
+ "bookmark", bookmark,
+ NULL));
+
+ return GTK_WIDGET (entry);
+}
+
+static void
+ephy_topics_entry_class_init (EphyTopicsEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->set_property = ephy_topics_entry_set_property;
+ object_class->constructor = ephy_topics_entry_constructor;
+ object_class->finalize = ephy_topics_entry_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_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property (object_class,
+ PROP_BOOKMARK,
+ g_param_spec_pointer ("bookmark",
+ "Bookmark",
+ "Bookmark",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private (object_class, sizeof(EphyTopicsEntryPrivate));
+}