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