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