2008-08-06 Claudio Saavedra <csaavedra@igalia.com>
[hildon] / src / hildon-touch-selector-entry.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2008 Nokia Corporation.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version. or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free
18  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "hildon-touch-selector.h"
22 #include "hildon-touch-selector-entry.h"
23
24 G_DEFINE_TYPE (HildonTouchSelectorEntry, hildon_touch_selector_entry, HILDON_TYPE_TOUCH_SELECTOR)
25
26 #define HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE(o) \
27   (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_TOUCH_SELECTOR_ENTRY, HildonTouchSelectorEntryPrivate))
28
29 typedef struct _HildonTouchSelectorEntryPrivate HildonTouchSelectorEntryPrivate;
30
31 static void entry_on_text_changed (GtkEditable * editable, gpointer userdata);
32 static void hildon_touch_selector_entry_changed (HildonTouchSelector * selector,
33                                                  gint column,
34                                                  gpointer user_data);
35 static void hildon_touch_selector_entry_set_model (HildonTouchSelector * selector,
36                                                    gint column, GtkTreeModel *model);
37 static gboolean hildon_touch_selector_entry_has_multiple_selection (HildonTouchSelector * selector);
38
39 struct _HildonTouchSelectorEntryPrivate {
40   gint text_column;
41   gulong signal_id;
42   GtkWidget *entry;
43 };
44
45 enum {
46   PROP_TEXT_COLUMN = 1
47 };
48
49 static void
50 hildon_touch_selector_entry_get_property (GObject *object, guint property_id,
51                                           GValue *value, GParamSpec *pspec)
52 {
53   switch (property_id) {
54   case PROP_TEXT_COLUMN:
55     g_value_set_int (value,
56                      hildon_touch_selector_entry_get_text_column (HILDON_TOUCH_SELECTOR_ENTRY (object)));
57   default:
58     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
59   }
60 }
61
62 static void
63 hildon_touch_selector_entry_set_property (GObject *object, guint property_id,
64                                           const GValue *value, GParamSpec *pspec)
65 {
66   switch (property_id) {
67   case PROP_TEXT_COLUMN:
68     hildon_touch_selector_entry_set_text_column (HILDON_TOUCH_SELECTOR_ENTRY (object),
69                                                  g_value_get_int (value));
70   default:
71     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
72   }
73 }
74
75 static void
76 hildon_touch_selector_entry_class_init (HildonTouchSelectorEntryClass *klass)
77 {
78   GObjectClass *object_class = G_OBJECT_CLASS (klass);
79   HildonTouchSelectorClass *selector_class = HILDON_TOUCH_SELECTOR_CLASS (klass);
80
81   g_type_class_add_private (klass, sizeof (HildonTouchSelectorEntryPrivate));
82
83   selector_class->set_model = hildon_touch_selector_entry_set_model;
84   selector_class->has_multiple_selection = hildon_touch_selector_entry_has_multiple_selection;
85
86   object_class->get_property = hildon_touch_selector_entry_get_property;
87   object_class->set_property = hildon_touch_selector_entry_set_property;
88
89   g_object_class_install_property (G_OBJECT_CLASS (klass),
90                                    PROP_TEXT_COLUMN,
91                                    g_param_spec_int ("text-column",
92                                                      "Text Column",
93                                                      "A column in the data source model to get the strings from.",
94                                                      -1,
95                                                      G_MAXINT,
96                                                      -1,
97                                                      G_PARAM_READWRITE));
98 }
99
100 static gchar *
101 hildon_touch_selector_entry_print_func (HildonTouchSelector * selector)
102 {
103   HildonTouchSelectorEntryPrivate *priv;
104
105   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
106
107   return g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry)));
108 }
109
110 static void
111 hildon_touch_selector_entry_init (HildonTouchSelectorEntry *self)
112 {
113   HildonTouchSelectorEntryPrivate *priv;
114   GtkEntryCompletion *completion;
115
116   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (self);
117
118   priv->entry = gtk_entry_new ();
119   gtk_entry_set_activates_default (GTK_ENTRY (priv->entry), TRUE);
120
121   completion = gtk_entry_completion_new ();
122   gtk_entry_completion_set_inline_completion (completion, TRUE);
123   gtk_entry_completion_set_popup_completion (completion, FALSE);
124   gtk_entry_set_completion (GTK_ENTRY (priv->entry), completion);
125
126   gtk_widget_show (priv->entry);
127   g_signal_connect (G_OBJECT (priv->entry), "changed",
128                     G_CALLBACK (entry_on_text_changed), self);
129   priv->signal_id = g_signal_connect (G_OBJECT (self), "changed",
130                                       G_CALLBACK (hildon_touch_selector_entry_changed), NULL);
131
132   hildon_touch_selector_set_print_func (HILDON_TOUCH_SELECTOR (self), hildon_touch_selector_entry_print_func);
133   gtk_box_pack_start (GTK_BOX (self), priv->entry, FALSE, FALSE, 0);
134 }
135
136 GtkWidget *
137 hildon_touch_selector_entry_new (void)
138 {
139   return g_object_new (HILDON_TYPE_TOUCH_SELECTOR_ENTRY, NULL);
140 }
141
142 /**
143  * hildon_touch_selector_entry_new_text:
144  * @void:
145  *
146  * Creates a #HildonTouchSelectorEntry with a single text column that
147  * can be populated conveniently through hildon_touch_selector_append_text(),
148  * hildon_touch_selector_prepend_text(), hildon_touch_selector_insert_text().
149  *
150  * Returns: A new #HildonTouchSelectorEntry
151  **/
152 GtkWidget *
153 hildon_touch_selector_entry_new_text (void)
154 {
155   GtkListStore *model;
156   GtkWidget *selector;
157   GtkEntryCompletion *completion;
158   HildonTouchSelectorEntryPrivate *priv;
159
160   selector = hildon_touch_selector_entry_new ();
161
162   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
163
164   model = gtk_list_store_new (1, G_TYPE_STRING);
165   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
166   gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (model));
167
168   hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
169                                             GTK_TREE_MODEL (model), FALSE);
170   hildon_touch_selector_entry_set_text_column (HILDON_TOUCH_SELECTOR_ENTRY (selector), 0);
171
172   return selector;
173 }
174
175 void
176 hildon_touch_selector_entry_set_text_column (HildonTouchSelectorEntry *selector,
177                                              gint text_column)
178 {
179   HildonTouchSelectorEntryPrivate *priv;
180   GtkEntryCompletion *completion;
181
182   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector));
183   g_return_if_fail (text_column >= -1);
184
185   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
186   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
187
188   gtk_entry_completion_set_text_column (completion, text_column);
189   priv->text_column = text_column;
190 }
191
192 gint
193 hildon_touch_selector_entry_get_text_column (HildonTouchSelectorEntry *selector)
194 {
195   HildonTouchSelectorEntryPrivate *priv;
196
197   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector), -1);
198
199   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
200
201   return priv->text_column;
202 }
203
204 static void
205 entry_on_text_changed (GtkEditable * editable,
206                        gpointer userdata)
207 {
208   HildonTouchSelector *selector;
209   HildonTouchSelectorEntryPrivate *priv;
210   GtkTreeModel *model;
211   GtkTreeIter iter;
212   GtkEntry *entry;
213   const gchar *prefix;
214   gchar *text;
215   gboolean found = FALSE;
216
217   entry = GTK_ENTRY (editable);
218   selector = HILDON_TOUCH_SELECTOR (userdata);
219   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
220
221   prefix = gtk_entry_get_text (entry);
222   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
223
224   if (!gtk_tree_model_get_iter_first (model, &iter)) {
225     return;
226   }
227
228   do {
229     gtk_tree_model_get (model, &iter, priv->text_column, &text, -1);
230     found = g_str_has_prefix (text, prefix);
231     g_free (text);
232   } while (found != TRUE && gtk_tree_model_iter_next (model, &iter));
233
234   g_signal_handler_block (selector, priv->signal_id);
235   {
236     /* We emit the HildonTouchSelector::changed signal because a change in the
237        GtkEntry represents a change in current selection, and therefore, users
238        should be notified. */
239     if (found) {
240       hildon_touch_selector_set_active_iter (selector, 0, &iter, TRUE);
241     }
242     g_signal_emit_by_name (selector, "changed", 0);
243   }
244   g_signal_handler_unblock (selector, priv->signal_id);
245
246 }
247
248 /* FIXME: This is actually a very ugly way to retrieve the text. Ideally,
249    we would have API to retrieve it from the base clase (HildonTouchSelector).
250    In the meantime, leaving it here.
251  */
252 static gchar *
253 hildon_touch_selector_get_text_from_model (HildonTouchSelectorEntry * selector)
254 {
255   HildonTouchSelectorEntryPrivate *priv;
256   GtkTreeModel *model;
257   GtkTreeIter iter;
258   GtkTreePath *path;
259   GList *selected_rows;
260   gchar *text;
261
262   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
263
264   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
265   selected_rows = hildon_touch_selector_get_selected_rows (HILDON_TOUCH_SELECTOR (selector), 0);
266
267   if (selected_rows == NULL) {
268     return NULL;
269   }
270
271   /* We are in single selection mode */
272   g_assert (selected_rows->next == NULL);
273
274   path = (GtkTreePath *)selected_rows->data;
275   gtk_tree_model_get_iter (model, &iter, path);
276   gtk_tree_model_get (model, &iter, priv->text_column, &text, -1);
277
278   gtk_tree_path_free (path);
279   g_list_free (selected_rows);
280
281   return text;
282 }
283
284 static void
285 hildon_touch_selector_entry_changed (HildonTouchSelector * selector,
286                                      gint column, gpointer user_data)
287 {
288   HildonTouchSelectorEntryPrivate *priv;
289   gchar *text;
290
291   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
292
293   text = hildon_touch_selector_get_text_from_model (HILDON_TOUCH_SELECTOR_ENTRY (selector));
294   gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
295   gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1);
296   g_free (text);
297 }
298
299 static void
300 hildon_touch_selector_entry_set_model (HildonTouchSelector * selector,
301                                        gint column, GtkTreeModel *model)
302 {
303   GtkEntryCompletion *completion;
304   HildonTouchSelectorEntryPrivate *priv;
305
306   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector));
307   g_return_if_fail (column == 0);
308   g_return_if_fail (GTK_IS_TREE_MODEL (model));
309
310   HILDON_TOUCH_SELECTOR_CLASS (hildon_touch_selector_entry_parent_class)->set_model (selector, column, model);
311
312   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
313
314   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
315   gtk_entry_completion_set_model (completion, model);
316   gtk_entry_completion_set_text_column (completion, priv->text_column);
317 }
318
319 static gboolean
320 hildon_touch_selector_entry_has_multiple_selection (HildonTouchSelector * selector)
321 {
322   /* Always TRUE, given the GtkEntry. */
323   return TRUE;
324 }