2009-01-13 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
27  * behaviour to #GtkComboBoxEntry, that allows user to select an item from a
28  * predefined list or to enter a different one in a #HildonEntry. Items can also
29  * be searched and selected 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
33  * column. 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 #include "hildon-entry.h"
49
50 G_DEFINE_TYPE (HildonTouchSelectorEntry, hildon_touch_selector_entry, HILDON_TYPE_TOUCH_SELECTOR)
51
52 #define HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE(o) \
53   (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_TOUCH_SELECTOR_ENTRY, HildonTouchSelectorEntryPrivate))
54
55 typedef struct _HildonTouchSelectorEntryPrivate HildonTouchSelectorEntryPrivate;
56
57 static void entry_on_text_changed (GtkEditable * editable, gpointer userdata);
58 static void hildon_touch_selector_entry_changed (HildonTouchSelector * selector,
59                                                  gint column,
60                                                  gpointer user_data);
61 static void hildon_touch_selector_entry_set_model (HildonTouchSelector * selector,
62                                                    gint column, GtkTreeModel *model);
63 static gboolean hildon_touch_selector_entry_has_multiple_selection (HildonTouchSelector * selector);
64
65 static void
66 _text_column_modified (GObject *pspec, GParamSpec *gobject, gpointer data);
67
68
69 struct _HildonTouchSelectorEntryPrivate {
70   gulong signal_id;
71   GtkWidget *entry;
72 };
73
74 enum {
75   PROP_TEXT_COLUMN = 1
76 };
77
78 static void
79 hildon_touch_selector_entry_get_property (GObject *object, guint property_id,
80                                           GValue *value, GParamSpec *pspec)
81 {
82   switch (property_id) {
83   case PROP_TEXT_COLUMN:
84     g_value_set_int (value,
85                      hildon_touch_selector_entry_get_text_column (HILDON_TOUCH_SELECTOR_ENTRY (object)));
86   default:
87     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
88   }
89 }
90
91 static void
92 hildon_touch_selector_entry_set_property (GObject *object, guint property_id,
93                                           const GValue *value, GParamSpec *pspec)
94 {
95   switch (property_id) {
96   case PROP_TEXT_COLUMN:
97     hildon_touch_selector_entry_set_text_column (HILDON_TOUCH_SELECTOR_ENTRY (object),
98                                                  g_value_get_int (value));
99   default:
100     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
101   }
102 }
103
104 static void
105 hildon_touch_selector_entry_class_init (HildonTouchSelectorEntryClass *klass)
106 {
107   GObjectClass *object_class = G_OBJECT_CLASS (klass);
108   HildonTouchSelectorClass *selector_class = HILDON_TOUCH_SELECTOR_CLASS (klass);
109
110   g_type_class_add_private (klass, sizeof (HildonTouchSelectorEntryPrivate));
111
112   selector_class->set_model = hildon_touch_selector_entry_set_model;
113   selector_class->has_multiple_selection = hildon_touch_selector_entry_has_multiple_selection;
114
115   object_class->get_property = hildon_touch_selector_entry_get_property;
116   object_class->set_property = hildon_touch_selector_entry_set_property;
117
118   /**
119    * HildonTouchSelectorEntry:text-column:
120    *
121    * Deprecated: now this property is in HildonTouchSelectorColumn use
122    * hildon_touch_selector_entry_set_text_column() and
123    * hildon_touch_selector_entry_get_text_column() to manage this.
124    *
125    * Since: 2.2
126    **/
127   g_object_class_install_property (G_OBJECT_CLASS (klass),
128                                    PROP_TEXT_COLUMN,
129                                    g_param_spec_int ("text-column",
130                                                      "Text Column",
131                                                      "A column in the data source model to get the strings from.",
132                                                      -1,
133                                                      G_MAXINT,
134                                                      -1,
135                                                      G_PARAM_READWRITE));
136 }
137
138 static gchar *
139 hildon_touch_selector_entry_print_func (HildonTouchSelector * selector)
140 {
141   HildonTouchSelectorEntryPrivate *priv;
142   GtkTreeModel *model;
143   GtkTreeIter iter;
144   gint column;
145   gchar *text = NULL;
146
147   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
148
149   if (*(gtk_entry_get_text (GTK_ENTRY (priv->entry))) != '\0') {
150     text = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry)));
151   } else {
152     model = hildon_touch_selector_get_model (selector, 0);
153     if (hildon_touch_selector_get_selected (selector, 0, &iter)) {
154       column = hildon_touch_selector_entry_get_text_column (HILDON_TOUCH_SELECTOR_ENTRY (selector));
155       gtk_tree_model_get (model, &iter, column, &text, -1);
156     }
157   }
158
159   return text;
160 }
161
162 static void
163 hildon_touch_selector_entry_init (HildonTouchSelectorEntry *self)
164 {
165   HildonTouchSelectorEntryPrivate *priv;
166   GtkEntryCompletion *completion;
167
168   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (self);
169
170   priv->entry = hildon_entry_new (HILDON_SIZE_AUTO);
171   gtk_entry_set_activates_default (GTK_ENTRY (priv->entry), TRUE);
172
173   completion = gtk_entry_completion_new ();
174   gtk_entry_completion_set_inline_completion (completion, TRUE);
175   gtk_entry_completion_set_popup_completion (completion, FALSE);
176   gtk_entry_set_completion (GTK_ENTRY (priv->entry), completion);
177
178   gtk_widget_show (priv->entry);
179   g_signal_connect (G_OBJECT (priv->entry), "changed",
180                     G_CALLBACK (entry_on_text_changed), self);
181   priv->signal_id = g_signal_connect (G_OBJECT (self), "changed",
182                                       G_CALLBACK (hildon_touch_selector_entry_changed), NULL);
183
184   hildon_touch_selector_set_print_func (HILDON_TOUCH_SELECTOR (self), hildon_touch_selector_entry_print_func);
185   gtk_box_pack_start (GTK_BOX (self), priv->entry, FALSE, FALSE, 0);
186 }
187
188 /**
189  * hildon_touch_selector_entry_new:
190  *
191  * Creates a #HildonTouchSelectorEntry
192  *
193  * Returns: A new #HildonTouchSelectorEntry
194  *
195  * Since: 2.2
196  **/
197 GtkWidget *
198 hildon_touch_selector_entry_new (void)
199 {
200   return g_object_new (HILDON_TYPE_TOUCH_SELECTOR_ENTRY, NULL);
201 }
202
203 /**
204  * hildon_touch_selector_entry_new_text:
205  *
206  * Creates a #HildonTouchSelectorEntry with a single text column that
207  * can be populated conveniently through hildon_touch_selector_append_text(),
208  * hildon_touch_selector_prepend_text(), hildon_touch_selector_insert_text().
209  *
210  * Returns: A new #HildonTouchSelectorEntry
211  *
212  * Since: 2.2
213  **/
214 GtkWidget *
215 hildon_touch_selector_entry_new_text (void)
216 {
217   GtkListStore *model;
218   GtkWidget *selector;
219   GtkEntryCompletion *completion;
220   HildonTouchSelectorEntryPrivate *priv;
221   HildonTouchSelectorColumn *column = NULL;
222
223   selector = hildon_touch_selector_entry_new ();
224
225   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
226
227   model = gtk_list_store_new (1, G_TYPE_STRING);
228   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
229   gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (model));
230   column = hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
231                                                      GTK_TREE_MODEL (model), FALSE);
232
233   g_signal_connect (column, "notify::text-column", G_CALLBACK (_text_column_modified),
234                     selector);
235   hildon_touch_selector_entry_set_text_column (HILDON_TOUCH_SELECTOR_ENTRY (selector), 0);
236
237   return selector;
238 }
239
240 static void
241 _text_column_modified (GObject *pspec, GParamSpec *gobject, gpointer data)
242 {
243   HildonTouchSelectorEntry *selector;
244   HildonTouchSelectorEntryPrivate *priv;
245   GtkEntryCompletion *completion;
246   gint text_column = -1;
247
248   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (data));
249   selector = HILDON_TOUCH_SELECTOR_ENTRY (data);
250
251   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (HILDON_TOUCH_SELECTOR_ENTRY(selector));
252   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
253
254   text_column = hildon_touch_selector_entry_get_text_column (selector);
255
256   gtk_entry_completion_set_text_column (completion, text_column);
257 }
258
259 /**
260  * hildon_touch_selector_entry_set_text_column:
261  * @selector: A #HildonTouchSelectorEntry
262  * @text_column: A column in model to get the strings from
263  *
264  * Sets the model column which touch selector box should use to get strings
265  * from to be @text_column.
266  *
267  * Since: 2.2
268  **/
269 void
270 hildon_touch_selector_entry_set_text_column (HildonTouchSelectorEntry *selector,
271                                              gint text_column)
272 {
273   HildonTouchSelectorColumn *column = NULL;
274
275   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector));
276   g_return_if_fail (text_column >= -1);
277
278   column = hildon_touch_selector_get_column (HILDON_TOUCH_SELECTOR (selector), 0);
279
280   g_object_set (G_OBJECT (column), "text-column", text_column, NULL);
281 }
282
283 /**
284  * hildon_touch_selector_entry_get_text_column:
285  * @selector: A #HildonTouchSelectorEntry
286  *
287  * Gets the text column that @selector is using as a text column.
288  *
289  * Returns: the number of the column used as a text column.
290  *
291  * Since: 2.2
292  **/
293 gint
294 hildon_touch_selector_entry_get_text_column (HildonTouchSelectorEntry *selector)
295 {
296   HildonTouchSelectorColumn *column = NULL;
297   gint text_column = -1;
298
299   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector), -1);
300
301   column = hildon_touch_selector_get_column (HILDON_TOUCH_SELECTOR (selector),
302                                              0);
303
304   g_object_get (G_OBJECT (column), "text-column", &text_column, NULL);
305
306   return text_column;
307 }
308
309 /**
310  * hildon_touch_selector_entry_set_input_mode:
311  * @selector: a #HildonTouchSelectorEntry
312  * @input_mode: #HildonGtkInputMode mask
313  *
314  * Sets the input mode to be used in the #GtkEntry in @selector. See hildon_gtk_entry_set_input_mode()
315  * for details.
316  *
317  * Since: 2.2
318  **/
319 void
320 hildon_touch_selector_entry_set_input_mode (HildonTouchSelectorEntry * selector,
321                                             HildonGtkInputMode input_mode)
322 {
323   HildonTouchSelectorEntryPrivate *priv;
324
325   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector));
326   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
327
328   hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry), input_mode);
329 }
330
331 /**
332  * hildon_touch_selector_entry_get_input_mode:
333  * @selector: a #HildonTouchSelectorEntry
334  *
335  * Gets the input mode used in the #GtkEntry in @selector. See hildon_gtk_entry_get_input_mode()
336  * for details.
337  *
338  * Returns: a mask of #HildonGtkInputMode
339  *
340  * Since: 2.2
341  **/
342 HildonGtkInputMode
343 hildon_touch_selector_entry_get_input_mode (HildonTouchSelectorEntry * selector)
344 {
345   HildonTouchSelectorEntryPrivate *priv;
346
347   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector), HILDON_GTK_INPUT_MODE_ALPHA);
348
349   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
350
351   return hildon_gtk_entry_get_input_mode (GTK_ENTRY (priv->entry));
352 }
353
354 static void
355 entry_on_text_changed (GtkEditable * editable,
356                        gpointer userdata)
357 {
358   HildonTouchSelector *selector;
359   HildonTouchSelectorEntryPrivate *priv;
360   GtkTreeModel *model;
361   GtkTreeIter iter;
362   GtkEntry *entry;
363   const gchar *prefix;
364   gchar *text;
365   gboolean found = FALSE;
366   gint text_column = -1;
367
368   entry = GTK_ENTRY (editable);
369   selector = HILDON_TOUCH_SELECTOR (userdata);
370   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
371
372   text_column =
373     hildon_touch_selector_entry_get_text_column (HILDON_TOUCH_SELECTOR_ENTRY (selector));
374
375   prefix = gtk_entry_get_text (entry);
376   model = hildon_touch_selector_get_model (selector, 0);
377
378   if (!gtk_tree_model_get_iter_first (model, &iter)) {
379     return;
380   }
381
382   do {
383     gtk_tree_model_get (model, &iter, text_column, &text, -1);
384     found = g_str_has_prefix (text, prefix);
385     g_free (text);
386   } while (found != TRUE && gtk_tree_model_iter_next (model, &iter));
387
388   g_signal_handler_block (selector, priv->signal_id);
389   {
390     /* We emit the HildonTouchSelector::changed signal because a change in the
391        GtkEntry represents a change in current selection, and therefore, users
392        should be notified. */
393     if (found) {
394       hildon_touch_selector_select_iter (selector, 0, &iter, TRUE);
395     }
396     g_signal_emit_by_name (selector, "changed", 0);
397   }
398   g_signal_handler_unblock (selector, priv->signal_id);
399
400 }
401
402 /* FIXME: This is actually a very ugly way to retrieve the text. Ideally,
403    we would have API to retrieve it from the base clase (HildonTouchSelector).
404    In the meantime, leaving it here.
405  */
406 static gchar *
407 hildon_touch_selector_get_text_from_model (HildonTouchSelectorEntry * selector)
408 {
409   HildonTouchSelectorEntryPrivate *priv;
410   GtkTreeModel *model;
411   GtkTreeIter iter;
412   GtkTreePath *path;
413   GList *selected_rows;
414   gchar *text;
415   gint text_column = -1;
416
417   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
418
419   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
420   text_column = hildon_touch_selector_entry_get_text_column (selector);
421   selected_rows = hildon_touch_selector_get_selected_rows (HILDON_TOUCH_SELECTOR (selector), 0);
422
423   if (selected_rows == NULL) {
424     return NULL;
425   }
426
427   /* We are in single selection mode */
428   g_assert (selected_rows->next == NULL);
429
430   path = (GtkTreePath *)selected_rows->data;
431   gtk_tree_model_get_iter (model, &iter, path);
432   gtk_tree_model_get (model, &iter, text_column, &text, -1);
433
434   gtk_tree_path_free (path);
435   g_list_free (selected_rows);
436
437   return text;
438 }
439
440 static void
441 hildon_touch_selector_entry_changed (HildonTouchSelector * selector,
442                                      gint column, gpointer user_data)
443 {
444   HildonTouchSelectorEntryPrivate *priv;
445   gchar *text;
446
447   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
448
449   text = hildon_touch_selector_get_text_from_model (HILDON_TOUCH_SELECTOR_ENTRY (selector));
450   if (text != NULL) {
451     gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
452     gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1);
453     g_free (text);
454   }
455 }
456
457 static void
458 hildon_touch_selector_entry_set_model (HildonTouchSelector * selector,
459                                        gint column, GtkTreeModel *model)
460 {
461   GtkEntryCompletion *completion;
462   HildonTouchSelectorEntryPrivate *priv;
463   gint text_column = -1;
464
465   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector));
466   g_return_if_fail (column == 0);
467   g_return_if_fail (GTK_IS_TREE_MODEL (model));
468
469   HILDON_TOUCH_SELECTOR_CLASS (hildon_touch_selector_entry_parent_class)->set_model (selector, column, model);
470
471   priv = HILDON_TOUCH_SELECTOR_ENTRY_GET_PRIVATE (selector);
472
473   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
474   gtk_entry_completion_set_model (completion, model);
475
476   text_column = hildon_touch_selector_entry_get_text_column (HILDON_TOUCH_SELECTOR_ENTRY (selector));
477
478   gtk_entry_completion_set_text_column (completion, text_column);
479 }
480
481 static gboolean
482 hildon_touch_selector_entry_has_multiple_selection (HildonTouchSelector * selector)
483 {
484   /* Always TRUE, given the GtkEntry. */
485   return TRUE;
486 }