[frogr] Added new widget to support autocompletion in a centralized way.



commit 6d177030ebb12b4f51f39af48ce3622f191a55a8
Author: Mario Sanchez Prada <msanchez igalia com>
Date:   Mon Aug 22 14:19:33 2011 +0200

    Added new widget to support autocompletion in a centralized way.
    
    This new widget (FrogrLiveEntry) will primarily be used for tag
    autocompletion both in the 'edit details' and 'add tags' dialogs, as
    it's just a subclass of GtkEntry containing the code so far duplicated
    both in the FrogrDetailsDialog and FrogrAddTagsDialog classes.

 src/Makefile.am        |    2 +
 src/frogr-live-entry.c |  259 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/frogr-live-entry.h |   56 +++++++++++
 3 files changed, 317 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 42589ed..2cfc7b0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -48,6 +48,8 @@ frogr_SOURCES = \
 	frogr-account.h \
 	frogr-add-tags-dialog.c \
 	frogr-add-tags-dialog.h \
+	frogr-live-entry.c \
+	frogr-live-entry.h \
 	frogr-add-to-set-dialog.c \
 	frogr-add-to-set-dialog.h \
 	frogr-add-to-group-dialog.c \
diff --git a/src/frogr-live-entry.c b/src/frogr-live-entry.c
new file mode 100644
index 0000000..6a77fd2
--- /dev/null
+++ b/src/frogr-live-entry.c
@@ -0,0 +1,259 @@
+/*
+ * frogr-live-entry.c -- Frogr 'live' entry (autocompletion enabled)
+ *
+ * Copyright (C) 2011 Mario Sanchez Prada
+ * Authors: Mario Sanchez Prada <msanchez igalia com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "frogr-live-entry.h"
+
+#include "frogr-global-defs.h"
+#include "frogr-picture.h"
+
+#include <config.h>
+#include <glib/gi18n.h>
+
+#define FROGR_LIVE_ENTRY_GET_PRIVATE(object)            \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((object),               \
+                                FROGR_TYPE_LIVE_ENTRY,  \
+                                FrogrLiveEntryPrivate))
+
+G_DEFINE_TYPE (FrogrLiveEntry, frogr_live_entry, GTK_TYPE_ENTRY)
+
+typedef struct _FrogrLiveEntryPrivate {
+  GtkEntryCompletion *entry_completion;
+  GtkTreeModel *treemodel;
+  gboolean auto_completion;
+} FrogrLiveEntryPrivate;
+
+
+/* Tree view columns */
+enum {
+  TEXT_COL,
+  N_COLS
+};
+
+
+/* Prototypes */
+
+static void _populate_treemodel_with_data (GtkTreeModel *treemodel, GSList *tags);
+
+static gboolean _tag_list_completion_func (GtkEntryCompletion *completion, const gchar *key,
+                                           GtkTreeIter *iter, gpointer data);
+
+static gboolean _completion_match_selected_cb (GtkEntryCompletion *widget, GtkTreeModel *model,
+                                               GtkTreeIter *iter, gpointer data);
+
+/* Private API */
+
+static void
+_populate_treemodel_with_data (GtkTreeModel *treemodel, GSList *tags)
+{
+  if (treemodel && tags)
+    {
+      GSList *item = NULL;
+      gchar *tag = NULL;
+      GtkTreeIter iter;
+
+      /* Initialize the list store */
+      for (item = tags; item; item = g_slist_next (item))
+        {
+          tag = (gchar *) item->data;
+          gtk_list_store_append (GTK_LIST_STORE (treemodel), &iter);
+          gtk_list_store_set (GTK_LIST_STORE (treemodel), &iter,
+                              0, tag, -1);
+        }
+    }
+  else if (treemodel)
+    {
+      /* Clear the list store */
+      gtk_list_store_clear (GTK_LIST_STORE (treemodel));
+    }
+}
+
+static gboolean
+_tag_list_completion_func (GtkEntryCompletion *completion, const gchar *key,
+                           GtkTreeIter *iter, gpointer data)
+{
+  FrogrLiveEntry *self = NULL;
+  FrogrLiveEntryPrivate *priv = NULL;
+  gchar *stripped_entry_text = NULL;
+  gchar *basetext = NULL;
+  gchar *tag = NULL;
+  gchar *lc_basetext = NULL;
+  gchar *lc_tag = NULL;
+  gint cursor_pos = 0;
+  gboolean matches = FALSE;
+
+  self = FROGR_LIVE_ENTRY (data);
+  priv = FROGR_LIVE_ENTRY_GET_PRIVATE (self);
+  gtk_tree_model_get (priv->treemodel, iter, TEXT_COL, &tag, -1);
+
+  /* Do nothing if not a valid tag */
+  if (!tag)
+    return FALSE;
+
+  /* Do nothing if the cursor is not in the last position */
+  cursor_pos = gtk_editable_get_position (GTK_EDITABLE (self));
+  if (cursor_pos < gtk_entry_get_text_length (GTK_ENTRY (self)))
+    return FALSE;
+
+  /* Look for the last token in 'key' */
+  stripped_entry_text = gtk_editable_get_chars (GTK_EDITABLE (self), 0, cursor_pos);
+  stripped_entry_text = g_strstrip (stripped_entry_text);
+  basetext = g_strrstr (stripped_entry_text, " ");
+  if (basetext)
+    basetext++;
+  else
+    basetext = stripped_entry_text;
+
+  /* Downcase everything and compare */
+  lc_basetext = g_utf8_strdown (basetext, -1);
+  lc_tag = g_utf8_strdown (tag, -1);
+  if (g_str_has_prefix (lc_tag, lc_basetext))
+    matches = TRUE;
+
+  g_free (stripped_entry_text);
+  g_free (tag);
+  g_free (lc_basetext);
+  g_free (lc_tag);
+
+  return matches;
+}
+
+static gboolean
+_completion_match_selected_cb (GtkEntryCompletion *widget, GtkTreeModel *model,
+                               GtkTreeIter *iter, gpointer data)
+{
+  FrogrLiveEntry *self = NULL;
+  gchar *tag = NULL;
+  const gchar *entry_text = NULL;
+  const gchar *matching_text = NULL;
+  gchar *base_text = NULL;
+  gchar *new_text = NULL;
+  glong entry_text_len = 0;
+  glong matching_text_len = 0;
+
+  self = FROGR_LIVE_ENTRY (data);
+  gtk_tree_model_get (model, iter, TEXT_COL, &tag, -1);
+
+  entry_text = gtk_entry_get_text (GTK_ENTRY (self));
+  matching_text = g_strrstr (entry_text, " ");
+  if (matching_text)
+    matching_text++;
+  else
+    matching_text = entry_text;
+
+  entry_text_len = gtk_entry_get_text_length (GTK_ENTRY (self));
+  matching_text_len = g_utf8_strlen (matching_text, -1);
+
+  base_text = gtk_editable_get_chars (GTK_EDITABLE (self), 0, entry_text_len - matching_text_len);
+  new_text = g_strdup_printf ("%s%s ", base_text, tag);
+
+  gtk_entry_set_text (GTK_ENTRY (self), new_text);
+  gtk_editable_set_position (GTK_EDITABLE (self), -1);
+
+  g_free (tag);
+  g_free (base_text);
+  g_free (new_text);
+
+  return TRUE;
+}
+
+static void
+_frogr_live_entry_dispose (GObject *object)
+{
+  FrogrLiveEntryPrivate *priv = FROGR_LIVE_ENTRY_GET_PRIVATE (object);
+
+  if (priv->entry_completion)
+    {
+      g_object_unref (priv->entry_completion);
+      priv->entry_completion = NULL;
+    }
+
+  if (priv->treemodel)
+    {
+      g_object_unref (priv->treemodel);
+      priv->treemodel = NULL;
+    }
+
+  G_OBJECT_CLASS(frogr_live_entry_parent_class)->dispose (object);
+}
+
+static void
+frogr_live_entry_class_init (FrogrLiveEntryClass *klass)
+{
+  GObjectClass *obj_class = (GObjectClass *)klass;
+
+  /* GObject signals */
+  obj_class->dispose = _frogr_live_entry_dispose;
+
+  g_type_class_add_private (obj_class, sizeof (FrogrLiveEntryPrivate));
+}
+
+static void
+frogr_live_entry_init (FrogrLiveEntry *self)
+{
+  FrogrLiveEntryPrivate *priv = FROGR_LIVE_ENTRY_GET_PRIVATE (self);
+
+  priv->entry_completion = NULL;
+  priv->treemodel = NULL;
+  priv->auto_completion = FALSE;
+}
+
+/* Public API */
+
+GtkWidget *
+frogr_live_entry_new (void)
+{
+  return GTK_WIDGET (g_object_new (FROGR_TYPE_LIVE_ENTRY, NULL));
+}
+
+void
+frogr_live_entry_set_auto_completion (FrogrLiveEntry *self, GSList *data)
+{
+  FrogrLiveEntryPrivate *priv = NULL;
+
+  priv = FROGR_LIVE_ENTRY_GET_PRIVATE (self);
+  priv->auto_completion = data ? TRUE : FALSE;
+
+  /* Initialize the auto-completion stuff if it's the first time */
+  if (priv->auto_completion && priv->entry_completion == NULL)
+    {
+      GtkTreeModel *model = NULL;
+
+      model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
+      priv->treemodel = model;
+
+      priv->entry_completion = gtk_entry_completion_new ();
+      gtk_entry_completion_set_text_column (GTK_ENTRY_COMPLETION (priv->entry_completion), TEXT_COL);
+      gtk_entry_completion_set_inline_completion (GTK_ENTRY_COMPLETION (priv->entry_completion), TRUE);
+      gtk_entry_completion_set_match_func (GTK_ENTRY_COMPLETION (priv->entry_completion),
+                                           _tag_list_completion_func,
+                                           self, NULL);
+
+      gtk_entry_completion_set_model (GTK_ENTRY_COMPLETION (priv->entry_completion), model);
+
+      gtk_entry_set_completion (GTK_ENTRY (self), priv->entry_completion);
+
+      g_signal_connect (G_OBJECT (priv->entry_completion), "match-selected",
+                        G_CALLBACK (_completion_match_selected_cb), self);
+    }
+
+  /* Populate the tree model with the data (or 'no data) passed */
+  if (priv->treemodel)
+    _populate_treemodel_with_data (priv->treemodel, data);
+}
diff --git a/src/frogr-live-entry.h b/src/frogr-live-entry.h
new file mode 100644
index 0000000..190dbd7
--- /dev/null
+++ b/src/frogr-live-entry.h
@@ -0,0 +1,56 @@
+/*
+ * frogr-live-entry.h -- Frogr 'live' entry (autocompletion enabled)
+ *
+ * Copyright (C) 2011 Mario Sanchez Prada
+ * Authors: Mario Sanchez Prada <msanchez igalia com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef FROGR_LIVE_ENTRY_H
+#define FROGR_LIVE_ENTRY_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define FROGR_TYPE_LIVE_ENTRY           (frogr_live_entry_get_type())
+#define FROGR_LIVE_ENTRY(obj)           (G_TYPE_CHECK_INSTANCE_CAST(obj, FROGR_TYPE_LIVE_ENTRY, FrogrLiveEntry))
+#define FROGR_LIVE_ENTRY_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST(klass, FROGR_TYPE_LIVE_ENTRY, FrogrLiveEntryClass))
+#define FROGR_IS_LIVE_ENTRY(obj)           (G_TYPE_CHECK_INSTANCE_TYPE(obj, FROGR_TYPE_LIVE_ENTRY))
+#define FROGR_IS_LIVE_ENTRY_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass), FROGR_TYPE_LIVE_ENTRY))
+#define FROGR_LIVE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FROGR_TYPE_LIVE_ENTRY, FrogrLiveEntryClass))
+
+typedef struct _FrogrLiveEntry        FrogrLiveEntry;
+typedef struct _FrogrLiveEntryClass   FrogrLiveEntryClass;
+
+struct _FrogrLiveEntryClass
+{
+  GtkEntryClass parent_class;
+};
+
+struct _FrogrLiveEntry
+{
+  GtkEntry parent;
+};
+
+GType frogr_live_entry_get_type (void) G_GNUC_CONST;
+
+GtkWidget *frogr_live_entry_new (void);
+
+void frogr_live_entry_set_auto_completion (FrogrLiveEntry *self, GSList *data);
+
+G_END_DECLS  /* FROGR_LIVE_ENTRY_H */
+
+#endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]