2008-09-16 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 /**
22  * SECTION:hildon-touch-selector-entry
23  * @short_description: A selector widget with one column and a text entry
24  * @see_also: #HildonTouchSelector, #HildonPickerButton
25  *
26  * #HildonTouchSelectorEntry is a selector widget with a text entry, similar in behaviour
27  * to #GtkComboBoxEntry, that allows user to select an item from a predefined list
28  * or to enter a different one in a #GtkEntry. Items can also be searched and selected
29  * by typing in the entry.
30  *
31  * The main difference between the #GtkTreeModel used by #HildonTouchSelector
32  * and #HildonTouchSelectorEntry, is that the latter must always include a text column.
33  * You should set it with hildon_touch_selector_entry_set_text_column().
34  *
35  * Normally, you would use #HildonTouchSelectorEntry together with a
36  * #HildonPickerDialog activated from a button. For the most common
37  * cases, you should use #HildonPickerButton.
38  *
39  * If you only need a text only, one column selector, you can create it with
40  * hildon_touch_selector_entry_new_text() and populate it with
41  * hildon_touch_selector_append_text(), hildon_touch_selector_prepend_text(),
42  * and hildon_touch_selector_insert_text().
43  *
44  */
45
46 #include "hildon-touch-selector.h"
47 #include "hildon-touch-selector-entry.h"
48
49 G_DEFINE_TYPE (HildonTouchSelectorEntry, hildon_touch_selector_entry, HILDON_TYPE_TOUCH_SELECTOR)
50
51 #define HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE(o) \
52   (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_TOUCH_SELECTOR_ENTRY, HildonTouchSelectorEntryPrivate))
53
54 typedef struct _HildonTouchSelectorEntryPrivate HildonTouchSelectorEntryPrivate;
55
56 static void entry_on_text_changed (GtkEditable * editable, gpointer userdata);
57 static void hildon_touch_selector_entry_changed (HildonTouchSelector * selector,
58                                                  gint column,
59                                                  gpointer user_data);
60 static void hildon_touch_selector_entry_set_model (HildonTouchSelector * selector,
61                                                    gint column, GtkTreeModel *model);
62 static gboolean hildon_touch_selector_entry_has_multiple_selection (HildonTouchSelector * selector);
63
64 struct _HildonTouchSelectorEntryPrivate {
65   gint text_column;
66   gulong signal_id;
67   GtkWidget *entry;
68 };
69
70 enum {
71   PROP_TEXT_COLUMN = 1
72 };
73
74 static void
75 hildon_touch_selector_entry_get_property (GObject *object, guint property_id,
76                                           GValue *value, GParamSpec *pspec)
77 {
78   switch (property_id) {
79   case PROP_TEXT_COLUMN:
80     g_value_set_int (value,
81                      hildon_touch_selector_entry_get_text_column (HILDON_TOUCH_SELECTOR_ENTRY (object)));
82   default:
83     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
84   }
85 }
86
87 static void
88 hildon_touch_selector_entry_set_property (GObject *object, guint property_id,
89                                           const GValue *value, GParamSpec *pspec)
90 {
91   switch (property_id) {
92   case PROP_TEXT_COLUMN:
93     hildon_touch_selector_entry_set_text_column (HILDON_TOUCH_SELECTOR_ENTRY (object),
94                                                  g_value_get_int (value));
95   default:
96     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
97   }
98 }
99
100 static void
101 hildon_touch_selector_entry_class_init (HildonTouchSelectorEntryClass *klass)
102 {
103   GObjectClass *object_class = G_OBJECT_CLASS (klass);
104   HildonTouchSelectorClass *selector_class = HILDON_TOUCH_SELECTOR_CLASS (klass);
105
106   g_type_class_add_private (klass, sizeof (HildonTouchSelectorEntryPrivate));
107
108   selector_class->set_model = hildon_touch_selector_entry_set_model;
109   selector_class->has_multiple_selection = hildon_touch_selector_entry_has_multiple_selection;
110
111   object_class->get_property = hildon_touch_selector_entry_get_property;
112   object_class->set_property = hildon_touch_selector_entry_set_property;
113
114   g_object_class_install_property (G_OBJECT_CLASS (klass),
115                                    PROP_TEXT_COLUMN,
116                                    g_param_spec_int ("text-column",
117                                                      "Text Column",
118                                                      "A column in the data source model to get the strings from.",
119                                                      -1,
120                                                      G_MAXINT,
121                                                      -1,
122                                                      G_PARAM_READWRITE));
123 }
124
125 static gchar *
126 hildon_touch_selector_entry_print_func (HildonTouchSelector * selector)
127 {
128   HildonTouchSelectorEntryPrivate *priv;
129
130   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
131
132   return g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry)));
133 }
134
135 static void
136 hildon_touch_selector_entry_init (HildonTouchSelectorEntry *self)
137 {
138   HildonTouchSelectorEntryPrivate *priv;
139   GtkEntryCompletion *completion;
140
141   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (self);
142
143   priv->entry = gtk_entry_new ();
144   gtk_entry_set_activates_default (GTK_ENTRY (priv->entry), TRUE);
145
146   completion = gtk_entry_completion_new ();
147   gtk_entry_completion_set_inline_completion (completion, TRUE);
148   gtk_entry_completion_set_popup_completion (completion, FALSE);
149   gtk_entry_set_completion (GTK_ENTRY (priv->entry), completion);
150
151   gtk_widget_show (priv->entry);
152   g_signal_connect (G_OBJECT (priv->entry), "changed",
153                     G_CALLBACK (entry_on_text_changed), self);
154   priv->signal_id = g_signal_connect (G_OBJECT (self), "changed",
155                                       G_CALLBACK (hildon_touch_selector_entry_changed), NULL);
156
157   hildon_touch_selector_set_print_func (HILDON_TOUCH_SELECTOR (self), hildon_touch_selector_entry_print_func);
158   gtk_box_pack_start (GTK_BOX (self), priv->entry, FALSE, FALSE, 0);
159 }
160
161 GtkWidget *
162 hildon_touch_selector_entry_new (void)
163 {
164   return g_object_new (HILDON_TYPE_TOUCH_SELECTOR_ENTRY, NULL);
165 }
166
167 /**
168  * hildon_touch_selector_entry_new_text:
169  *
170  * Creates a #HildonTouchSelectorEntry with a single text column that
171  * can be populated conveniently through hildon_touch_selector_append_text(),
172  * hildon_touch_selector_prepend_text(), hildon_touch_selector_insert_text().
173  *
174  * Returns: A new #HildonTouchSelectorEntry
175  **/
176 GtkWidget *
177 hildon_touch_selector_entry_new_text (void)
178 {
179   GtkListStore *model;
180   GtkWidget *selector;
181   GtkEntryCompletion *completion;
182   HildonTouchSelectorEntryPrivate *priv;
183
184   selector = hildon_touch_selector_entry_new ();
185
186   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
187
188   model = gtk_list_store_new (1, G_TYPE_STRING);
189   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
190   gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (model));
191
192   hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
193                                             GTK_TREE_MODEL (model), FALSE);
194   hildon_touch_selector_entry_set_text_column (HILDON_TOUCH_SELECTOR_ENTRY (selector), 0);
195
196   return selector;
197 }
198
199 void
200 hildon_touch_selector_entry_set_text_column (HildonTouchSelectorEntry *selector,
201                                              gint text_column)
202 {
203   HildonTouchSelectorEntryPrivate *priv;
204   GtkEntryCompletion *completion;
205
206   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector));
207   g_return_if_fail (text_column >= -1);
208
209   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
210   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
211
212   gtk_entry_completion_set_text_column (completion, text_column);
213   priv->text_column = text_column;
214 }
215
216 gint
217 hildon_touch_selector_entry_get_text_column (HildonTouchSelectorEntry *selector)
218 {
219   HildonTouchSelectorEntryPrivate *priv;
220
221   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector), -1);
222
223   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
224
225   return priv->text_column;
226 }
227
228 static void
229 entry_on_text_changed (GtkEditable * editable,
230                        gpointer userdata)
231 {
232   HildonTouchSelector *selector;
233   HildonTouchSelectorEntryPrivate *priv;
234   GtkTreeModel *model;
235   GtkTreeIter iter;
236   GtkEntry *entry;
237   const gchar *prefix;
238   gchar *text;
239   gboolean found = FALSE;
240
241   entry = GTK_ENTRY (editable);
242   selector = HILDON_TOUCH_SELECTOR (userdata);
243   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
244
245   prefix = gtk_entry_get_text (entry);
246   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
247
248   if (!gtk_tree_model_get_iter_first (model, &iter)) {
249     return;
250   }
251
252   do {
253     gtk_tree_model_get (model, &iter, priv->text_column, &text, -1);
254     found = g_str_has_prefix (text, prefix);
255     g_free (text);
256   } while (found != TRUE && gtk_tree_model_iter_next (model, &iter));
257
258   g_signal_handler_block (selector, priv->signal_id);
259   {
260     /* We emit the HildonTouchSelector::changed signal because a change in the
261        GtkEntry represents a change in current selection, and therefore, users
262        should be notified. */
263     if (found) {
264       hildon_touch_selector_select_iter (selector, 0, &iter, TRUE);
265     }
266     g_signal_emit_by_name (selector, "changed", 0);
267   }
268   g_signal_handler_unblock (selector, priv->signal_id);
269
270 }
271
272 /* FIXME: This is actually a very ugly way to retrieve the text. Ideally,
273    we would have API to retrieve it from the base clase (HildonTouchSelector).
274    In the meantime, leaving it here.
275  */
276 static gchar *
277 hildon_touch_selector_get_text_from_model (HildonTouchSelectorEntry * selector)
278 {
279   HildonTouchSelectorEntryPrivate *priv;
280   GtkTreeModel *model;
281   GtkTreeIter iter;
282   GtkTreePath *path;
283   GList *selected_rows;
284   gchar *text;
285
286   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
287
288   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
289   selected_rows = hildon_touch_selector_get_selected_rows (HILDON_TOUCH_SELECTOR (selector), 0);
290
291   if (selected_rows == NULL) {
292     return NULL;
293   }
294
295   /* We are in single selection mode */
296   g_assert (selected_rows->next == NULL);
297
298   path = (GtkTreePath *)selected_rows->data;
299   gtk_tree_model_get_iter (model, &iter, path);
300   gtk_tree_model_get (model, &iter, priv->text_column, &text, -1);
301
302   gtk_tree_path_free (path);
303   g_list_free (selected_rows);
304
305   return text;
306 }
307
308 static void
309 hildon_touch_selector_entry_changed (HildonTouchSelector * selector,
310                                      gint column, gpointer user_data)
311 {
312   HildonTouchSelectorEntryPrivate *priv;
313   gchar *text;
314
315   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
316
317   text = hildon_touch_selector_get_text_from_model (HILDON_TOUCH_SELECTOR_ENTRY (selector));
318   if (text != NULL) {
319     gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
320     gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1);
321     g_free (text);
322   }
323 }
324
325 static void
326 hildon_touch_selector_entry_set_model (HildonTouchSelector * selector,
327                                        gint column, GtkTreeModel *model)
328 {
329   GtkEntryCompletion *completion;
330   HildonTouchSelectorEntryPrivate *priv;
331
332   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector));
333   g_return_if_fail (column == 0);
334   g_return_if_fail (GTK_IS_TREE_MODEL (model));
335
336   HILDON_TOUCH_SELECTOR_CLASS (hildon_touch_selector_entry_parent_class)->set_model (selector, column, model);
337
338   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
339
340   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
341   gtk_entry_completion_set_model (completion, model);
342   gtk_entry_completion_set_text_column (completion, priv->text_column);
343 }
344
345 static gboolean
346 hildon_touch_selector_entry_has_multiple_selection (HildonTouchSelector * selector)
347 {
348   /* Always TRUE, given the GtkEntry. */
349   return TRUE;
350 }