[libgweather] GWeatherLocationEntry: autocomplete on activate



commit c8db4e162b29fd7f74293bfb79a39c31b2d6fcba
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Fri Mar 15 23:51:47 2013 +0100

    GWeatherLocationEntry: autocomplete on activate
    
    Extend GWeatherLocationEntry so that when only one completion is possible,
    we insert it when pressing enter, and then propagate the activation up.
    At the same time, and without breaking backward compatibility of documented
    API, add a private structure.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=695761

 libgweather/location-entry.c |  132 +++++++++++++++++++++++++++++++++++-------
 libgweather/location-entry.h |   17 ++++--
 2 files changed, 121 insertions(+), 28 deletions(-)
---
diff --git a/libgweather/location-entry.c b/libgweather/location-entry.c
index 594b796..310bf44 100644
--- a/libgweather/location-entry.c
+++ b/libgweather/location-entry.c
@@ -36,6 +36,13 @@
  * #GWeatherLocation<!-- -->s
  */
 
+struct _GWeatherLocationEntryPrivate {
+    GtkTreeModel     *filter_model;
+    GWeatherLocation *location;
+    GWeatherLocation *top;
+    gboolean          custom_text;
+};
+
 G_DEFINE_TYPE (GWeatherLocationEntry, gweather_location_entry, GTK_TYPE_ENTRY)
 
 enum {
@@ -54,6 +61,11 @@ static void set_property (GObject *object, guint prop_id,
 static void get_property (GObject *object, guint prop_id,
                          GValue *value, GParamSpec *pspec);
 
+static void set_location_internal (GWeatherLocationEntry *entry,
+                                  GtkTreeModel          *model,
+                                  GtkTreeIter           *iter,
+                                  GWeatherLocation      *loc);
+
 enum
 {
     GWEATHER_LOCATION_ENTRY_COL_DISPLAY_NAME = 0,
@@ -72,9 +84,28 @@ static gboolean match_selected (GtkEntryCompletion *completion,
 static void     entry_changed (GWeatherLocationEntry *entry);
 
 static void
+hack_filter_model_data_func (GtkCellLayout   *layout,
+                            GtkCellRenderer *renderer,
+                            GtkTreeModel    *model,
+                            GtkTreeIter     *iter,
+                            gpointer         data)
+{
+    GWeatherLocationEntry *self;
+    GWeatherLocationEntryPrivate *priv;
+
+    self = GWEATHER_LOCATION_ENTRY (gtk_entry_completion_get_entry (GTK_ENTRY_COMPLETION (layout)));
+    priv = self->priv;
+
+    priv->filter_model = model;
+}
+
+static void
 gweather_location_entry_init (GWeatherLocationEntry *entry)
 {
     GtkEntryCompletion *completion;
+    GWeatherLocationEntryPrivate *priv;
+
+    priv = entry->priv = G_TYPE_INSTANCE_GET_PRIVATE (entry, GWEATHER_TYPE_LOCATION_ENTRY, 
GWeatherLocationEntryPrivate);
 
     completion = gtk_entry_completion_new ();
 
@@ -82,13 +113,30 @@ gweather_location_entry_init (GWeatherLocationEntry *entry)
     gtk_entry_completion_set_text_column (completion, GWEATHER_LOCATION_ENTRY_COL_DISPLAY_NAME);
     gtk_entry_completion_set_match_func (completion, matcher, NULL, NULL);
 
-    g_signal_connect (completion, "match_selected",
+    /* Giant Hack!
+       We want to grab the filter model used internally by GtkEntryCompletion.
+       We know that it is "leaked" by a few functions and signals, such
+       as the CellDataFunc in GtkCellLayout. So we set that in a way that
+       when it gets called, it sets the filter model where we can access it.
+    */
+    {
+       GList *renderers;
+
+       renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (completion));
+       gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion),
+                                           renderers->data,
+                                           hack_filter_model_data_func, NULL, NULL);
+
+       g_list_free (renderers);
+    }
+
+    g_signal_connect (completion, "match-selected",
                      G_CALLBACK (match_selected), entry);
 
     gtk_entry_set_completion (GTK_ENTRY (entry), completion);
     g_object_unref (completion);
 
-    entry->custom_text = FALSE;
+    priv->custom_text = FALSE;
     g_signal_connect (entry, "changed",
                      G_CALLBACK (entry_changed), NULL);
 }
@@ -96,25 +144,60 @@ gweather_location_entry_init (GWeatherLocationEntry *entry)
 static void
 finalize (GObject *object)
 {
-    GWeatherLocationEntry *entry = GWEATHER_LOCATION_ENTRY (object);
+    GWeatherLocationEntry *entry;
+    GWeatherLocationEntryPrivate *priv;
+
+    entry = GWEATHER_LOCATION_ENTRY (object);
+    priv = entry->priv;
 
-    if (entry->location)
-       gweather_location_unref (entry->location);
-    if (entry->top)
-       gweather_location_unref (entry->top);
+    if (priv->location)
+       gweather_location_unref (priv->location);
+    if (priv->top)
+       gweather_location_unref (priv->top);
 
     G_OBJECT_CLASS (gweather_location_entry_parent_class)->finalize (object);
 }
 
 static void
+gweather_location_entry_activate (GtkEntry *entry)
+{
+    GWeatherLocationEntry *self;
+    GWeatherLocationEntryPrivate *priv;
+    GtkEntryCompletion *completion;
+
+    self = GWEATHER_LOCATION_ENTRY (entry);
+    priv = self->priv;
+
+    completion = gtk_entry_get_completion (entry);
+    gtk_entry_completion_complete (completion);
+
+    if (priv->custom_text &&
+       gtk_tree_model_iter_n_children (priv->filter_model, NULL) == 1) {
+       GtkTreeIter iter, real_iter;
+
+       gtk_tree_model_get_iter_first (priv->filter_model, &iter);
+       gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
+                                                         &real_iter, &iter);
+
+       set_location_internal (self, gtk_entry_completion_get_model (completion),
+                              &real_iter, NULL);
+    }
+
+    GTK_ENTRY_CLASS (gweather_location_entry_parent_class)->activate (entry);
+}
+
+static void
 gweather_location_entry_class_init (GWeatherLocationEntryClass *location_entry_class)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (location_entry_class);
+    GtkEntryClass *entry_class = GTK_ENTRY_CLASS (location_entry_class);
 
     object_class->finalize = finalize;
     object_class->set_property = set_property;
     object_class->get_property = get_property;
 
+    entry_class->activate = gweather_location_entry_activate;
+
     /* properties */
     g_object_class_install_property (
        object_class, PROP_TOP,
@@ -130,6 +213,8 @@ gweather_location_entry_class_init (GWeatherLocationEntryClass *location_entry_c
                            "The selected GWeatherLocation",
                            GWEATHER_TYPE_LOCATION,
                            G_PARAM_READWRITE));
+
+    g_type_class_add_private (location_entry_class, sizeof (GWeatherLocationEntryPrivate));
 }
 
 static void
@@ -159,7 +244,7 @@ get_property (GObject *object, guint prop_id,
 
     switch (prop_id) {
     case PROP_LOCATION:
-       g_value_set_boxed (value, entry->location);
+       g_value_set_boxed (value, entry->priv->location);
        break;
     default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -170,7 +255,7 @@ get_property (GObject *object, guint prop_id,
 static void
 entry_changed (GWeatherLocationEntry *entry)
 {
-    entry->custom_text = TRUE;
+    entry->priv->custom_text = TRUE;
 }
 
 static void
@@ -179,10 +264,13 @@ set_location_internal (GWeatherLocationEntry *entry,
                       GtkTreeIter           *iter,
                       GWeatherLocation      *loc)
 {
+    GWeatherLocationEntryPrivate *priv;
     char *name;
 
-    if (entry->location)
-       gweather_location_unref (entry->location);
+    priv = entry->priv;
+
+    if (priv->location)
+       gweather_location_unref (priv->location);
 
     g_assert (iter == NULL || loc == NULL);
 
@@ -191,21 +279,21 @@ set_location_internal (GWeatherLocationEntry *entry,
                            GWEATHER_LOCATION_ENTRY_COL_DISPLAY_NAME, &name,
                            GWEATHER_LOCATION_ENTRY_COL_LOCATION, &loc,
                            -1);
-       entry->location = gweather_location_ref (loc);
+       priv->location = gweather_location_ref (loc);
        gtk_entry_set_text (GTK_ENTRY (entry), name);
-       entry->custom_text = FALSE;
+       priv->custom_text = FALSE;
        g_free (name);
     } else if (loc) {
-       entry->location = gweather_location_ref (loc);
+       priv->location = gweather_location_ref (loc);
        gtk_entry_set_text (GTK_ENTRY (entry), loc->name);
-       entry->custom_text = TRUE;
+       priv->custom_text = TRUE;
     } else {
-       entry->location = NULL;
+       priv->location = NULL;
        gtk_entry_set_text (GTK_ENTRY (entry), "");
-       entry->custom_text = TRUE;
+       priv->custom_text = TRUE;
     }
 
-    gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
+    gtk_editable_set_position (GTK_EDITABLE (entry), -1);
     g_object_notify (G_OBJECT (entry), "location");
 }
 
@@ -264,8 +352,8 @@ gweather_location_entry_get_location (GWeatherLocationEntry *entry)
 {
     g_return_val_if_fail (GWEATHER_IS_LOCATION_ENTRY (entry), NULL);
 
-    if (entry->location)
-       return gweather_location_ref (entry->location);
+    if (entry->priv->location)
+       return gweather_location_ref (entry->priv->location);
     else
        return NULL;
 }
@@ -286,7 +374,7 @@ gweather_location_entry_has_custom_text (GWeatherLocationEntry *entry)
 {
     g_return_val_if_fail (GWEATHER_IS_LOCATION_ENTRY (entry), FALSE);
 
-    return entry->custom_text;
+    return entry->priv->custom_text;
 }
 
 /**
@@ -463,7 +551,7 @@ gweather_location_entry_build_model (GWeatherLocationEntry *entry,
 {
     GtkTreeStore *store = NULL;
 
-    entry->top = gweather_location_ref (top);
+    entry->priv->top = gweather_location_ref (top);
 
     store = gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING);
     fill_location_entry_model (store, top, NULL, NULL);
diff --git a/libgweather/location-entry.h b/libgweather/location-entry.h
index 919d22b..4f3147e 100644
--- a/libgweather/location-entry.h
+++ b/libgweather/location-entry.h
@@ -24,6 +24,10 @@
 #include <gtk/gtk.h>
 #include <libgweather/gweather-location.h>
 
+typedef struct _GWeatherLocationEntry GWeatherLocationEntry;
+typedef struct _GWeatherLocationEntryClass GWeatherLocationEntryClass;
+typedef struct _GWeatherLocationEntryPrivate GWeatherLocationEntryPrivate;
+
 #define GWEATHER_TYPE_LOCATION_ENTRY            (gweather_location_entry_get_type ())
 #define GWEATHER_LOCATION_ENTRY(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), 
GWEATHER_TYPE_LOCATION_ENTRY, GWeatherLocationEntry))
 #define GWEATHER_LOCATION_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GWEATHER_TYPE_LOCATION_ENTRY, GWeatherLocationEntryClass))
@@ -31,18 +35,19 @@
 #define GWEATHER_IS_LOCATION_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GWEATHER_TYPE_LOCATION_ENTRY))
 #define GWEATHER_LOCATION_ENTRY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GWEATHER_TYPE_LOCATION_ENTRY, GWeatherLocationEntryClass))
 
-typedef struct {
+struct _GWeatherLocationEntry {
     GtkEntry parent;
 
     /*< private >*/
-    GWeatherLocation *location, *top;
-    guint custom_text : 1;
-} GWeatherLocationEntry;
+    GWeatherLocationEntryPrivate *priv;
+    gpointer backward_compatibility_padding;
+    guint more_padding : 1;
+};
 
-typedef struct {
+struct _GWeatherLocationEntryClass {
     GtkEntryClass parent_class;
 
-} GWeatherLocationEntryClass;
+};
 
 GType             gweather_location_entry_get_type     (void);
 


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