Allow to use HILDON_UI_MODE_NORMAL on the HildonTouchSelector
[hildon] / hildon / hildon-touch-selector.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 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
23  * @short_description: A selector widget with several columns.
24  *
25  * #HildonTouchSelector is a selector widget, that allows users to
26  * select items from one to many predefined lists. It is very similar
27  * to #GtkComboBox, but with several individual pannable columns.
28  *
29  * Normally, you would use #HildonTouchSelector together with a
30  * #HildonPickerDialog activated from a button. For the most common
31  * cases, you should use #HildonPickerButton.
32  *
33  * The contents of each #HildonTouchSelector column are stored in a
34  * #GtkTreeModel. To add a new column to a #HildonTouchSelector, use
35  * hildon_touch_selector_append_column(). If you want to add a
36  * text-only column, without special attributes, use
37  * hildon_touch_selector_append_text_column().
38  *
39  * It is highly recommended that you use only one column
40  * #HildonTouchSelector<!-- -->s.
41  * If you only need a text only, one column selector, you can create it with
42  * hildon_touch_selector_new_text() and populate with
43  * hildon_touch_selector_append_text(), hildon_touch_selector_prepend_text(),
44  * and hildon_touch_selector_insert_text().
45  *
46  * If you need a selector widget that also accepts user inputs, you
47  * can use #HildonTouchSelectorEntry.
48  *
49  * The current selection has a string representation. In the most common cases,
50  * each column model will contain a text column. You can configure
51  * which column in particular using the #HildonTouchSelectorColumn property
52  * #HildonTouchSelectorColumn:text-column
53  *
54  * You can get this string representation using
55  * hildon_touch_selector_get_current_text().
56  * You can configure how the selection is printed with
57  * hildon_touch_selector_set_print_func(), that sets the current hildon touch
58  * selector print function. The widget has a default print function, that
59  * uses the #HildonTouchSelectorColumn:text-column property on each
60  * #HildonTouchSelectorColumn to compose the final representation.
61  *
62  * If you create the selector using hildon_touch_selector_new_text() you
63  * don't need to take care of this property, as the model is created internally.
64  * If you create the selector using hildon_touch_selector_new(), you
65  * need to specify properly the property for your custom model in order to get a
66  * non-empty string representation, or define your custom print function.
67  *
68  * <example>
69  * <title>Creating a HildonTouchSelector</title>
70  * <programlisting>
71  * void
72  * selection_changed (HildonTouchSelector * selector,
73  *                    gpointer *user_data)
74  * {
75  *   gchar *current_selection = NULL;
76  * <!-- -->
77  *   current_selection = hildon_touch_selector_get_current_text (selector);
78  *   g_debug ("Current selection : &percnt;s", current_selection);
79  * }
80  * <!-- -->
81  * static GtkWidget *
82  * create_customized_selector ()
83  * {
84  *   GtkWidget *selector = NULL;
85  *   GSList *icon_list = NULL;
86  *   GtkListStore *store_icons = NULL;
87  *   GSList *item = NULL;
88  *   GtkCellRenderer *renderer = NULL;
89  *   HildonTouchSelectorColumn *column = NULL;
90  * <!-- -->
91  *   selector = hildon_touch_selector_new ();
92  * <!-- -->
93  *   icon_list = gtk_stock_list_ids ();
94  * <!-- -->
95  *   store_icons = gtk_list_store_new (1, G_TYPE_STRING);
96  *   for (item = icon_list; item; item = g_slist_next (item)) {
97  *     GtkTreeIter iter;
98  *     gchar *label = item->data;
99  * <!-- -->
100  *     gtk_list_store_append (store_icons, &amp;iter);
101  *     gtk_list_store_set (store_icons, &amp;iter, 0, label, -1);
102  *     g_free (label);
103  *   }
104  *   g_slist_free (icon_list);
105  * <!-- -->
106  *   renderer = gtk_cell_renderer_pixbuf_new ();
107  *   gtk_cell_renderer_set_fixed_size (renderer, -1, 100);
108  * <!-- -->
109  *   column = hildon_touch_selector_append_column (HILDON_TOUCH_SELECTOR (selector),
110  *                                                 GTK_TREE_MODEL (store_icons),
111  *                                                 renderer, "stock-id", 0, NULL);
112  * <!-- -->
113  *   hildon_touch_selector_column_set_text_column (column, 0);
114  * <!-- -->
115  *   hildon_touch_selector_set_column_selection_mode (HILDON_TOUCH_SELECTOR (selector),
116  *                                                    HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE);
117  * <!-- -->
118  *   g_signal_connect (G_OBJECT (selector), "changed",
119  *                     G_CALLBACK (selection_changed), NULL);
120  * <!-- -->
121  *   return selector;
122  * }
123  * <!-- -->
124  * static GtkWidget *
125  * create_simple_selector ()
126  * {
127  *   GtkWidget *selector = NULL;
128  *   gint i;
129  * <!-- -->
130  *   selector = hildon_touch_selector_new_text ();
131  *   hildon_touch_selector_set_column_selection_mode (HILDON_TOUCH_SELECTOR (selector),
132  *                                                    HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE);
133  * <!-- -->
134  *   g_signal_connect (G_OBJECT (selector), "changed",
135  *                     G_CALLBACK (selection_changed), NULL);
136  * <!-- -->
137  *   for (i = 1; i <= 10 ; i++) {
138  *     gchar *label = g_strdup_printf ("Item &amp;percnt;d", i);
139  * <!-- -->
140  *     hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
141  *                                        label);
142  * <!-- -->
143  *     g_free (label);
144  *   }
145  * <!-- -->
146  *   return selector;
147  * }
148  * </programlisting>
149  * </example>
150  */
151
152 /**
153  * SECTION:hildon-touch-selector-column
154  * @short_description: A visible column in a #HildonTouchSelector
155  * @see_also: #HildonTouchSelector
156  *
157  * A #HildonTouchSelectorColumn is a column in a
158  * #HildonTouchSelector. This class implements the #GtkCellLayout interface, allowing
159  * a flexible management of the cellrenderers in each #HildonTouchSelector column.
160  */
161
162 #undef HILDON_DISABLE_DEPRECATED
163
164 #ifdef HAVE_CONFIG_H
165 #include <config.h>
166 #endif
167
168 #include <string.h>
169 #include <stdlib.h>
170
171 #include "hildon-gtk.h"
172
173 #include "hildon-pannable-area.h"
174 #include "hildon-touch-selector.h"
175 #include "hildon-touch-selector-private.h"
176
177 #define HILDON_TOUCH_SELECTOR_GET_PRIVATE(obj)                          \
178   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_TOUCH_SELECTOR, HildonTouchSelectorPrivate))
179
180 G_DEFINE_TYPE (HildonTouchSelector, hildon_touch_selector, GTK_TYPE_VBOX)
181
182 /*
183  * IMPLEMENTATION NOTES:
184  * Struct to maintain the data of each column. The columns are the elements
185  * of the widget that belongs properly to the selection behaviour. Although
186  * internally the columns are arranged in a private #GtkHBox, as the selector
187  * itself is a #GtkVBox, you can add more widgets, like buttons etc., so
188  * you finally could have a widget with more elements that the columns, but
189  * this doesn't belongs to the selection logic
190  */
191 struct _HildonTouchSelectorColumnPrivate
192 {
193   HildonTouchSelector *parent;    /* the selector that contains this column */
194   GtkTreeModel *model;
195   gint text_column;
196   GtkTreeView *tree_view;
197   gulong realize_handler;
198   GtkTreePath *initial_path;
199
200   GtkWidget *panarea;           /* the pannable widget */
201   GtkTreeRowReference *last_activated;
202 };
203
204 struct _HildonTouchSelectorPrivate
205 {
206   GSList *columns;              /* the selection columns */
207   GtkWidget *hbox;              /* the container for the selector's columns */
208   gboolean initial_scroll;      /* whether initial fancy scrolling to selection */
209
210   gboolean changed_blocked;
211
212   HildonTouchSelectorPrintFunc print_func;
213   gpointer print_user_data;
214   GDestroyNotify print_destroy_func;
215
216   HildonUIMode hildon_ui_mode;
217 };
218
219 enum
220 {
221   PROP_HAS_MULTIPLE_SELECTION = 1,
222   PROP_INITIAL_SCROLL,
223   PROP_HILDON_UI_MODE
224 };
225
226 enum
227 {
228   CHANGED,
229   COLUMNS_CHANGED,
230   LAST_SIGNAL
231 };
232
233 static gint hildon_touch_selector_signals[LAST_SIGNAL] = { 0 };
234
235 static void
236 hildon_touch_selector_dispose                   (GObject * object);
237
238 static void
239 hildon_touch_selector_get_property              (GObject * object,
240                                                  guint prop_id,
241                                                  GValue * value,
242                                                  GParamSpec * pspec);
243 static void
244 hildon_touch_selector_set_property              (GObject *object,
245                                                  guint prop_id,
246                                                  const GValue *value,
247                                                  GParamSpec *pspec);
248 /* gtkwidget */
249
250 /* gtkcontainer */
251 static void hildon_touch_selector_remove        (GtkContainer * container,
252                                                  GtkWidget * widget);
253 /* private functions */
254 static void _row_tapped_cb                      (GtkTreeView * tree_view,
255                                                  GtkTreePath * path,
256                                                  gpointer user_data);
257 static void
258 hildon_touch_selector_row_activated_cb          (GtkTreeView       *tree_view,
259                                                  GtkTreePath       *path,
260                                                  GtkTreeViewColumn *column,
261                                                  gpointer           user_data);
262
263 static gchar *_default_print_func               (HildonTouchSelector * selector,
264                                                  gpointer user_data);
265
266 static HildonTouchSelectorColumn *_create_new_column (HildonTouchSelector * selector,
267                                                  GtkTreeModel * model,
268                                                  gboolean *emit_changed,
269                                                  GtkCellRenderer * renderer,
270                                                  va_list args);
271 static gboolean
272 on_realize_cb                                  (GtkWidget *widget,
273                                                 gpointer data);
274 static void
275 on_row_changed                                 (GtkTreeModel *model,
276                                                 GtkTreePath *path,
277                                                 GtkTreeIter *iter,
278                                                 gpointer userdata);
279
280 static void
281 hildon_touch_selector_scroll_to (HildonTouchSelectorColumn *column,
282                                  GtkTreeView *tv,
283                                  GtkTreePath *path);
284 static gboolean
285 _hildon_touch_selector_center_on_selected_items (HildonTouchSelector *selector,
286                                                  HildonTouchSelectorColumn *column);
287 static void
288 _hildon_touch_selector_set_model                (HildonTouchSelector * selector,
289                                                  gint num_column,
290                                                  GtkTreeModel * model);
291 static gboolean
292 _hildon_touch_selector_has_multiple_selection   (HildonTouchSelector * selector);
293
294 static void
295 hildon_touch_selector_emit_value_changed        (HildonTouchSelector *selector,
296                                                  gint column);
297
298 /* GtkCellLayout implementation (HildonTouchSelectorColumn)*/
299 static void hildon_touch_selector_column_cell_layout_init         (GtkCellLayoutIface      *iface);
300
301 static void hildon_touch_selector_column_cell_layout_pack_start   (GtkCellLayout         *cell_layout,
302                                                                    GtkCellRenderer       *cell,
303                                                                    gboolean               expand);
304 static void hildon_touch_selector_column_cell_layout_pack_end     (GtkCellLayout         *cell_layout,
305                                                                    GtkCellRenderer       *cell,
306                                                                    gboolean               expand);
307 static void hildon_touch_selector_column_cell_layout_clear        (GtkCellLayout         *cell_layout);
308 static void hildon_touch_selector_column_cell_layout_add_attribute(GtkCellLayout         *cell_layout,
309                                                                    GtkCellRenderer       *cell,
310                                                                    const gchar           *attribute,
311                                                                    gint                   column);
312 static void hildon_touch_selector_column_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
313                                                                          GtkCellRenderer       *cell,
314                                                                          GtkCellLayoutDataFunc  func,
315                                                                          gpointer               func_data,
316                                                                          GDestroyNotify         destroy);
317 static void hildon_touch_selector_column_cell_layout_clear_attributes   (GtkCellLayout         *cell_layout,
318                                                                          GtkCellRenderer       *cell);
319 static void hildon_touch_selector_column_cell_layout_reorder       (GtkCellLayout         *cell_layout,
320                                                                     GtkCellRenderer       *cell,
321                                                                     gint                   position);
322 static GList *hildon_touch_selector_column_cell_layout_get_cells   (GtkCellLayout         *cell_layout);
323
324 static void
325 hildon_touch_selector_check_ui_mode_coherence   (HildonTouchSelector *selector);
326
327 static void
328 hildon_touch_selector_class_init (HildonTouchSelectorClass * class)
329 {
330   GObjectClass *gobject_class;
331   GtkObjectClass *object_class;
332   GtkContainerClass *container_class;
333
334   gobject_class = G_OBJECT_CLASS (class);
335   object_class = GTK_OBJECT_CLASS (class);
336   container_class = GTK_CONTAINER_CLASS (class);
337
338   /* GObject */
339   gobject_class->dispose = hildon_touch_selector_dispose;
340   gobject_class->get_property = hildon_touch_selector_get_property;
341   gobject_class->set_property = hildon_touch_selector_set_property;
342
343   /* GtkWidget */
344
345   /* GtkContainer */
346   container_class->remove = hildon_touch_selector_remove;
347
348   /* HildonTouchSelector */
349   class->changed = NULL;
350   class->set_model = _hildon_touch_selector_set_model;
351
352   class->has_multiple_selection = _hildon_touch_selector_has_multiple_selection;
353
354   /* signals */
355   /**
356    * HildonTouchSelector::changed:
357    * @widget: the object which received the signal
358    * @column: the number of the column that has changed
359    *
360    * The "changed" signal is emitted when the active item on any column is changed.
361    * This can be due to the user selecting a different item from the list, or
362    * due to a call to hildon_touch_selector_select_iter() on one of the columns.
363    *
364    * Since: 2.2
365    */
366   hildon_touch_selector_signals[CHANGED] =
367     g_signal_new ("changed",
368                   G_OBJECT_CLASS_TYPE (class),
369                   G_SIGNAL_RUN_LAST,
370                   G_STRUCT_OFFSET (HildonTouchSelectorClass, changed),
371                   NULL, NULL,
372                   g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
373
374   /**
375    * HildonTouchSelector::columns-changed:
376    * @selector: the object which received the signal
377    *
378    * The "columns-changed" signal is emitted when the number
379    * of columns in the #HildonTouchSelector change.
380    *
381    * Since: 2.2
382    */
383   hildon_touch_selector_signals[COLUMNS_CHANGED] =
384     g_signal_new ("columns-changed",
385                   G_OBJECT_CLASS_TYPE (class),
386                   G_SIGNAL_RUN_LAST, 0,
387                   NULL, NULL,
388                   g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
389
390   /* properties */
391
392   g_object_class_install_property (gobject_class, PROP_HAS_MULTIPLE_SELECTION,
393                                    g_param_spec_boolean ("has-multiple-selection",
394                                                          "has multiple selection",
395                                                          "Whether the widget has multiple "
396                                                          "selection (like multiple columns, "
397                                                          "multiselection mode, or multiple "
398                                                          "internal widgets) and therefore "
399                                                          "it may need a confirmation button, "
400                                                          "for instance.",
401                                                          FALSE,
402                                                          G_PARAM_READABLE));
403
404   g_object_class_install_property (G_OBJECT_CLASS (gobject_class),
405                                    PROP_INITIAL_SCROLL,
406                                    g_param_spec_boolean ("initial-scroll",
407                                                          "Initial scroll",
408                                                          "Whether to scroll to the"
409                                                          "current selection when"
410                                                          "the selector is first"
411                                                          "shown",
412                                                          TRUE,
413                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
414
415     /**
416      * HildonTouchSelector:hildon-ui-mode:
417      *
418      * Specifies which UI mode to use in the internal treeviews.  A setting
419      * of %HILDON_UI_MODE_NORMAL will cause these tree view to disable selections
420      * and emit row-activated as soon as a row is pressed.  You can use the
421      * method hildon_touch_selector_get_last_activated_row() to get it. When
422      * %HILDON_UI_MODE_EDIT is set, selections can be made according to the
423      * setting of the mode on GtkTreeSelection.
424      *
425      * Toggling this property will cause the tree view to select an
426      * appropriate selection mode if not already done.
427      *
428      * Since: Hildon 2.2
429      */
430   g_object_class_install_property (gobject_class,
431                                    PROP_HILDON_UI_MODE,
432                                    g_param_spec_enum ("hildon-ui-mode",
433                                                       "Hildon UI Mode",
434                                                       "The Hildon UI mode according "
435                                                       "to which the touch selector "
436                                                       "should behave",
437                                                       HILDON_TYPE_UI_MODE,
438                                                       HILDON_UI_MODE_EDIT,
439                                                       G_PARAM_READWRITE));
440   /* style properties */
441   /* We need to ensure fremantle mode for the treeview in order to work
442      properly. This is not about the appearance, this is about behaviour */
443   gtk_rc_parse_string ("style \"fremantle-htst\" {\n"
444                        "  GtkWidget::hildon-mode = 1\n"
445                        "} widget \"*.fremantle-htst\" style \"fremantle-htst\""
446                        "widget_class \"*<HildonPannableArea>.GtkTreeView\" style :highest \"fremantle-htst\"");
447
448   g_type_class_add_private (object_class, sizeof (HildonTouchSelectorPrivate));
449 }
450
451 static void
452 hildon_touch_selector_get_property (GObject * object,
453                                     guint prop_id,
454                                     GValue * value, GParamSpec * pspec)
455 {
456   HildonTouchSelectorPrivate *priv = HILDON_TOUCH_SELECTOR (object)->priv;
457
458   switch (prop_id) {
459   case PROP_HAS_MULTIPLE_SELECTION:
460     g_value_set_boolean (value,
461                          hildon_touch_selector_has_multiple_selection (HILDON_TOUCH_SELECTOR (object)));
462     break;
463   case PROP_INITIAL_SCROLL:
464     g_value_set_boolean (value, priv->initial_scroll);
465     break;
466   case PROP_HILDON_UI_MODE:
467     g_value_set_enum (value, priv->hildon_ui_mode);
468     break;
469   default:
470     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
471     break;
472   }
473 }
474
475 static void
476 hildon_touch_selector_set_property (GObject *object, guint prop_id,
477                                     const GValue *value, GParamSpec *pspec)
478 {
479   HildonTouchSelectorPrivate *priv = HILDON_TOUCH_SELECTOR (object)->priv;
480
481   switch (prop_id) {
482   case PROP_INITIAL_SCROLL:
483     priv->initial_scroll = g_value_get_boolean (value);
484     break;
485   case PROP_HILDON_UI_MODE:
486     hildon_touch_selector_set_hildon_ui_mode (HILDON_TOUCH_SELECTOR (object),
487                                               g_value_get_enum (value));
488     break;
489   default:
490     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
491     break;
492   }
493 }
494
495
496 static void
497 hildon_touch_selector_init (HildonTouchSelector * selector)
498 {
499   selector->priv = HILDON_TOUCH_SELECTOR_GET_PRIVATE (selector);
500
501   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (selector), GTK_NO_WINDOW);
502   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (selector), FALSE);
503
504   selector->priv->columns = NULL;
505
506   selector->priv->print_func = NULL;
507   selector->priv->print_user_data = NULL;
508   selector->priv->print_destroy_func = NULL;
509   selector->priv->initial_scroll = TRUE;
510   selector->priv->hbox = gtk_hbox_new (FALSE, 0);
511
512   selector->priv->changed_blocked = FALSE;
513
514   selector->priv->hildon_ui_mode = HILDON_UI_MODE_EDIT;
515
516   gtk_box_pack_end (GTK_BOX (selector), selector->priv->hbox,
517                     TRUE, TRUE, 0);
518   gtk_widget_show (selector->priv->hbox);
519 }
520
521 static void
522 hildon_touch_selector_dispose (GObject * object)
523 {
524   GObjectClass *gobject_class;
525
526   hildon_touch_selector_set_print_func_full (HILDON_TOUCH_SELECTOR (object),
527                                              NULL, NULL, NULL);
528
529   gobject_class = G_OBJECT_CLASS (hildon_touch_selector_parent_class);
530
531   if (gobject_class->dispose)
532     (* gobject_class->dispose) (object);
533 }
534
535 static void
536 clean_column                                    (HildonTouchSelectorColumn *col,
537                                                  HildonTouchSelector *selector)
538 {
539   g_signal_handlers_disconnect_by_func (col->priv->model,
540                                         on_row_changed, selector);
541
542   if (col->priv->last_activated != NULL) {
543     gtk_tree_row_reference_free (col->priv->last_activated);
544     col->priv->last_activated = NULL;
545   }
546 }
547
548 /*
549  * IMPLEMENTATION NOTES:
550  * Some people sent questions regarding the fact that the dispose/finalize functions
551  * doesn't clean the internal widgets that could lead to leak memory, so we will
552  * clarify this topic.
553  *
554  * This is not required as #HildonTouchSelector extends #GtkContainer. When the
555  * widget is freed, the #GtkContainer freeing memory functions are called. This
556  * process includes remove each widget individually, so all the widgets are
557  * properly freed.
558  *
559  * In the same way, this widget redefines gtk_container->remove function, in
560  * order to free the column related information if it is required.
561  *
562  * Please take a look to hildon_touch_selector_remove for more information.
563  */
564
565 /*------------------------------ GtkContainer ------------------------------ */
566
567 /*
568  * Required in order to free the column at the columns list
569  */
570 static void
571 hildon_touch_selector_remove (GtkContainer * container, GtkWidget * widget)
572 {
573   HildonTouchSelector *selector = NULL;
574
575   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (container));
576   selector = HILDON_TOUCH_SELECTOR (container);
577
578   /* Remove the extra data related to the columns, if required. */
579   if (widget == selector->priv->hbox) {
580     g_slist_foreach (selector->priv->columns, (GFunc) clean_column, selector);
581     g_slist_foreach (selector->priv->columns, (GFunc) g_object_unref, NULL);
582
583     g_slist_free (selector->priv->columns);
584
585     selector->priv->columns = NULL;
586   } else {
587     g_debug ("Freeing a widget outside the columns logic");
588   }
589
590   /* Now remove the widget itself from the container */
591   GTK_CONTAINER_CLASS (hildon_touch_selector_parent_class)->remove (container, widget);
592 }
593
594 /* ------------------------------ PRIVATE METHODS ---------------------------- */
595 void
596 hildon_touch_selector_block_changed             (HildonTouchSelector *selector)
597 {
598   selector->priv->changed_blocked = TRUE;
599 }
600
601 void
602 hildon_touch_selector_unblock_changed           (HildonTouchSelector *selector)
603 {
604   selector->priv->changed_blocked = FALSE;
605 }
606
607 static void
608 hildon_touch_selector_emit_value_changed        (HildonTouchSelector *selector,
609                                                  gint column)
610 {
611   /* FIXME: it could be good to emit too the GtkTreePath of the element
612      selected, as now it is required to connect to the signal and then ask
613      for the element selected. We can't do this API change, in order to avoid
614      and ABI break */
615   if (!selector->priv->changed_blocked) {
616     g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, column);
617   }
618 }
619
620 static void
621 hildon_touch_selector_check_ui_mode_coherence   (HildonTouchSelector *selector)
622 {
623   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
624
625   if (hildon_touch_selector_get_num_columns (selector) > 1) {
626     hildon_touch_selector_set_hildon_ui_mode (selector, HILDON_UI_MODE_EDIT);
627   }
628 }
629
630 /**
631  * default_print_func:
632  * @selector: a #HildonTouchSelector
633  *
634  * Default print function
635  *
636  * Returns: a new string that represents the selected items
637  *
638  * Since: 2.2
639  **/
640 static gchar *
641 _default_print_func (HildonTouchSelector * selector, gpointer user_data)
642 {
643   gchar *result = NULL;
644   gchar *aux = NULL;
645   gint num_columns = 0;
646   GtkTreeIter iter;
647   GtkTreeModel *model = NULL;
648   gchar *current_string = NULL;
649   gint i;
650   HildonTouchSelectorSelectionMode mode;
651   GList *item = NULL;
652   GtkTreePath *current_path = NULL;
653   GList *selected_rows = NULL;
654   gint initial_value = 0;
655   gint text_column = -1;
656   HildonTouchSelectorColumn *column = NULL;
657
658   num_columns = hildon_touch_selector_get_num_columns (selector);
659
660   mode = hildon_touch_selector_get_column_selection_mode (selector);
661
662   if ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
663       && (num_columns > 0)) {
664     /* In this case we get the first column first */
665     selected_rows = hildon_touch_selector_get_selected_rows (selector, 0);
666     model = hildon_touch_selector_get_model (selector, 0);
667     column = hildon_touch_selector_get_column (selector, 0);
668     text_column = hildon_touch_selector_column_get_text_column (column);
669
670     result = g_strdup_printf ("(");
671     i = 0;
672     for (item = selected_rows; item; item = g_list_next (item)) {
673       current_path = item->data;
674       gtk_tree_model_get_iter (model, &iter, current_path);
675
676       if (text_column != -1) {
677         gtk_tree_model_get (model, &iter, text_column, &current_string, -1);
678       }
679
680       if (i < g_list_length (selected_rows) - 1) {
681         aux = g_strconcat (result, current_string, ",", NULL);
682         g_free (result);
683         result = aux;
684       } else {
685         aux = g_strconcat (result, current_string, NULL);
686         g_free (result);
687         result = aux;
688       }
689
690       if (current_string) {
691         g_free (current_string);
692         current_string = NULL;
693       }
694
695       i++;
696     }
697
698     aux = g_strconcat (result, ")", NULL);
699     g_free (result);
700     result = aux;
701
702     g_list_foreach (selected_rows, (GFunc) (gtk_tree_path_free), NULL);
703     g_list_free (selected_rows);
704     initial_value = 1;
705   } else {
706     initial_value = 0;
707   }
708
709   for (i = initial_value; i < num_columns; i++) {
710     model = hildon_touch_selector_get_model (selector, i);
711     column = hildon_touch_selector_get_column (selector, i);
712     text_column = hildon_touch_selector_column_get_text_column (column);
713
714     if (hildon_touch_selector_get_selected (selector, i, &iter)) {
715       if (text_column == -1 ) {
716         g_warning ("Trying to use the default print function in HildonTouchSelector, but "
717                    "\"text-column\" property is not set for HildonTouchSelectorColumn %p.", column);
718         current_string = NULL;
719       } else {
720         gtk_tree_model_get (model, &iter, text_column, &current_string, -1);
721       }
722
723       if (i == 0) {
724         result = current_string;
725       } else {
726         aux = g_strconcat (result, ":", current_string, NULL);
727         g_free (result);
728         g_free (current_string);
729         current_string = NULL;
730         result = aux;
731       }
732     }
733   }
734
735   return result;
736 }
737
738 static void
739 hildon_touch_selector_row_activated_cb          (GtkTreeView       *tree_view,
740                                                  GtkTreePath       *path,
741                                                  GtkTreeViewColumn *column,
742                                                  gpointer           user_data)
743 {
744   HildonTouchSelectorColumn *selector_column = NULL;
745   GtkTreeModel *model = NULL;
746
747   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (user_data));
748   selector_column = HILDON_TOUCH_SELECTOR_COLUMN (user_data);
749
750   model = selector_column->priv->model;
751
752   if (selector_column->priv->last_activated != NULL) {
753     gtk_tree_row_reference_free (selector_column->priv->last_activated);
754   }
755
756   selector_column->priv->last_activated = gtk_tree_row_reference_new (model, path);
757 }
758
759 static void
760 _row_tapped_cb (GtkTreeView * tree_view, GtkTreePath * path, gpointer user_data)
761 {
762   HildonTouchSelector *selector = NULL;
763   HildonTouchSelectorColumn *column = NULL;
764   gint num_column = -1;
765
766   column = HILDON_TOUCH_SELECTOR_COLUMN (user_data);
767   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (column->priv->parent));
768
769   selector = column->priv->parent;
770
771   num_column = g_slist_index (selector->priv->columns, column);
772
773   hildon_touch_selector_emit_value_changed (selector, num_column);
774 }
775
776
777 static HildonTouchSelectorColumn *
778 _create_new_column (HildonTouchSelector * selector,
779                     GtkTreeModel * model,
780                     gboolean *emit_changed,
781                     GtkCellRenderer * renderer, va_list args)
782 {
783   HildonTouchSelectorColumn *new_column = NULL;
784   GtkTreeViewColumn *tree_column = NULL;
785   GtkTreeView *tv = NULL;
786   GtkWidget *panarea = NULL;
787   GtkTreeSelection *selection = NULL;
788   GtkTreeIter iter;
789   gchar *attribute;
790   gint value;
791
792   tree_column = gtk_tree_view_column_new ();
793
794   if (renderer != NULL) {
795     gtk_tree_view_column_pack_start (tree_column, renderer, TRUE);
796
797     attribute = va_arg (args, gchar *);
798     while (attribute != NULL) {
799       value = va_arg (args, gint);
800       gtk_tree_view_column_add_attribute (tree_column, renderer, attribute,
801                                           value);
802       attribute = va_arg (args, gchar *);
803     }
804   }
805
806 #ifdef MAEMO_GTK
807   tv = GTK_TREE_VIEW (hildon_gtk_tree_view_new (selector->priv->hildon_ui_mode));
808 #else
809   tv = GTK_TREE_VIEW (gtk_tree_view_new ());
810 #endif /* MAEMO_GTK */
811
812   gtk_tree_view_set_enable_search (tv, FALSE);
813   GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (tv), GTK_CAN_FOCUS);
814
815   gtk_tree_view_set_model (tv, model);
816   g_signal_connect (model, "row-changed",
817                     G_CALLBACK (on_row_changed), selector);
818   gtk_tree_view_set_rules_hint (tv, TRUE);
819
820   gtk_tree_view_append_column (GTK_TREE_VIEW (tv), tree_column);
821
822   new_column = g_object_new (HILDON_TYPE_TOUCH_SELECTOR_COLUMN, NULL);
823   new_column->priv->parent = selector;
824
825   panarea = hildon_pannable_area_new ();
826
827   g_object_set (G_OBJECT (panarea),
828                 "initial-hint", FALSE, NULL);
829
830   gtk_container_add (GTK_CONTAINER (panarea), GTK_WIDGET (tv));
831
832   new_column->priv->model = model;
833   new_column->priv->tree_view = tv;
834   new_column->priv->panarea = panarea;
835   new_column->priv->realize_handler = 0;
836   new_column->priv->initial_path = NULL;
837
838   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
839   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
840
841   /* select the first item */
842   *emit_changed = FALSE;
843   if ((gtk_tree_model_get_iter_first (model, &iter))&&
844       (selector->priv->hildon_ui_mode == HILDON_UI_MODE_EDIT)) {
845     gtk_tree_selection_select_iter (selection, &iter);
846     *emit_changed = TRUE;
847   }
848
849   gtk_widget_grab_focus (GTK_WIDGET (tv));
850
851   /* connect to the hildon-row-tapped signal connection */
852   g_signal_connect (G_OBJECT (tv), "hildon-row-tapped",
853                     G_CALLBACK (_row_tapped_cb), new_column);
854
855   g_signal_connect (G_OBJECT (tv), "row-activated",
856                     G_CALLBACK (hildon_touch_selector_row_activated_cb), new_column);
857
858   return new_column;
859 }
860
861
862 /* ------------------------ HildonTouchSelectorColumn implementation ---------------------- */
863 G_DEFINE_TYPE_WITH_CODE (HildonTouchSelectorColumn, hildon_touch_selector_column, G_TYPE_OBJECT,
864                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
865                                                 hildon_touch_selector_column_cell_layout_init))
866
867 enum
868 {
869   PROP_TEXT_COLUMN = 1
870 };
871
872 static void
873 hildon_touch_selector_column_class_init (HildonTouchSelectorColumnClass *klass);
874
875 static void
876 hildon_touch_selector_column_get_property (GObject *object, guint property_id,
877                                            GValue *value, GParamSpec *pspec);
878
879 static void
880 hildon_touch_selector_column_set_property  (GObject *object, guint property_id,
881                                             const GValue *value, GParamSpec *pspec);
882
883
884 static void
885 hildon_touch_selector_column_class_init (HildonTouchSelectorColumnClass *klass)
886 {
887   GObjectClass *gobject_class = NULL;
888
889   gobject_class = G_OBJECT_CLASS (klass);
890
891   g_type_class_add_private (gobject_class, sizeof (HildonTouchSelectorColumnPrivate));
892
893   /* GObject */
894   gobject_class->get_property = hildon_touch_selector_column_get_property;
895   gobject_class->set_property = hildon_touch_selector_column_set_property;
896
897   /**
898    * HildonTouchSelectorColumn:text-column:
899    *
900    * A column in the data source model to get the strings from.
901    *
902    * Since: maemo 2.2
903    **/
904   g_object_class_install_property (G_OBJECT_CLASS(klass),
905                                    PROP_TEXT_COLUMN,
906                                    g_param_spec_int ("text-column",
907                                                      "Text Column",
908                                                      "A column in the data source model to get the strings from.",
909                                                      -1,
910                                                      G_MAXINT,
911                                                      -1,
912                                                      G_PARAM_READWRITE));
913 }
914
915 static void
916 hildon_touch_selector_column_init (HildonTouchSelectorColumn *column)
917 {
918   column->priv = G_TYPE_INSTANCE_GET_PRIVATE (column, HILDON_TYPE_TOUCH_SELECTOR_COLUMN,
919                                               HildonTouchSelectorColumnPrivate);
920   column->priv->text_column = -1;
921   column->priv->last_activated = NULL;
922 }
923
924 /**
925  * hildon_touch_selector_column_set_text_column:
926  * @column: A #HildonTouchSelectorColumn
927  * @text_column: the index of a model column in the model for @column.
928  *
929  * Sets the model column to be displayed in @column. @text_column must point to a
930  * column in the model used with type %G_TYPE_STRING. Initially, this property
931  * is unset. You should set it before using the #HildonTouchSelector that uses
932  * @column.
933  *
934  * Since: 2.2
935  **/
936 void
937 hildon_touch_selector_column_set_text_column (HildonTouchSelectorColumn *column,
938                                               gint text_column)
939 {
940   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (column));
941   g_return_if_fail (text_column >= -1);
942
943   column->priv->text_column = text_column;
944
945   g_object_notify (G_OBJECT (column), "text-column");
946 }
947
948 /**
949  * hildon_touch_selector_column_get_text_column:
950  * @column: a #HildonTouchSelectorColumn
951  *
952  * Gets the model column set as the text source for @column.
953  *
954  * Returns: the index of the text column for @column, or -1 if unset.
955  *
956  * Since: 2.2
957  **/
958 gint
959 hildon_touch_selector_column_get_text_column (HildonTouchSelectorColumn *column)
960 {
961   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (column), -1);
962
963   return column->priv->text_column;
964 }
965
966 static void
967 hildon_touch_selector_column_get_property (GObject *object, guint property_id,
968                                            GValue *value, GParamSpec *pspec)
969 {
970   switch (property_id) {
971   case PROP_TEXT_COLUMN:
972     g_value_set_int (value,
973                      hildon_touch_selector_column_get_text_column (HILDON_TOUCH_SELECTOR_COLUMN (object)));
974     break;
975   default:
976     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
977   }
978 }
979
980 static void
981 hildon_touch_selector_column_set_property (GObject *object, guint property_id,
982                                            const GValue *value, GParamSpec *pspec)
983 {
984   switch (property_id) {
985   case PROP_TEXT_COLUMN:
986     hildon_touch_selector_column_set_text_column (HILDON_TOUCH_SELECTOR_COLUMN (object),
987                                                   g_value_get_int (value));
988     break;
989   default:
990     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
991   }
992 }
993
994 /* ------------------------ GtkCellLayout implementation -------------------- */
995 static void
996 hildon_touch_selector_column_cell_layout_init (GtkCellLayoutIface      *iface)
997 {
998   iface->pack_start         = hildon_touch_selector_column_cell_layout_pack_start;
999   iface->pack_end           = hildon_touch_selector_column_cell_layout_pack_end;
1000   iface->clear              = hildon_touch_selector_column_cell_layout_clear;
1001   iface->add_attribute      = hildon_touch_selector_column_cell_layout_add_attribute;
1002   iface->set_cell_data_func = hildon_touch_selector_column_cell_layout_set_cell_data_func;
1003   iface->clear_attributes   = hildon_touch_selector_column_cell_layout_clear_attributes;
1004   iface->reorder            = hildon_touch_selector_column_cell_layout_reorder;
1005   iface->get_cells          = hildon_touch_selector_column_cell_layout_get_cells;
1006 }
1007
1008 static void
1009 hildon_touch_selector_column_cell_layout_pack_start (GtkCellLayout         *cell_layout,
1010                                                GtkCellRenderer       *cell,
1011                                                gboolean               expand)
1012 {
1013   HildonTouchSelectorColumn *sel_column = NULL;
1014   GtkTreeViewColumn *view_column = NULL;
1015
1016   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1017   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1018
1019   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1020
1021   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(view_column), cell, expand);
1022
1023 }
1024
1025 static void
1026 hildon_touch_selector_column_cell_layout_pack_end (GtkCellLayout         *cell_layout,
1027                                              GtkCellRenderer       *cell,
1028                                              gboolean               expand)
1029 {
1030   HildonTouchSelectorColumn *sel_column = NULL;
1031   GtkTreeViewColumn *view_column = NULL;
1032
1033   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1034   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1035
1036   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1037
1038   gtk_cell_layout_pack_end (GTK_CELL_LAYOUT(view_column), cell, expand);
1039 }
1040
1041 static void
1042 hildon_touch_selector_column_cell_layout_clear (GtkCellLayout         *cell_layout)
1043 {
1044   HildonTouchSelectorColumn *sel_column = NULL;
1045   GtkTreeViewColumn *view_column = NULL;
1046
1047   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1048   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1049
1050   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1051
1052   gtk_cell_layout_clear (GTK_CELL_LAYOUT(view_column));
1053 }
1054
1055 static void
1056 hildon_touch_selector_column_cell_layout_add_attribute (GtkCellLayout         *cell_layout,
1057                                                   GtkCellRenderer       *cell,
1058                                                   const gchar           *attribute,
1059                                                   gint                   column)
1060 {
1061   HildonTouchSelectorColumn *sel_column = NULL;
1062   GtkTreeViewColumn *view_column = NULL;
1063
1064   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1065   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1066
1067   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1068
1069   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(view_column), cell, attribute, column);
1070 }
1071
1072
1073 static void
1074 hildon_touch_selector_column_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
1075                                                        GtkCellRenderer       *cell,
1076                                                        GtkCellLayoutDataFunc  func,
1077                                                        gpointer               func_data,
1078                                                        GDestroyNotify         destroy)
1079 {
1080   HildonTouchSelectorColumn *sel_column = NULL;
1081   GtkTreeViewColumn *view_column = NULL;
1082
1083   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1084   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1085
1086   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1087
1088   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(view_column), cell, func,
1089                                       func_data, destroy);
1090 }
1091
1092 static void
1093 hildon_touch_selector_column_cell_layout_clear_attributes (GtkCellLayout         *cell_layout,
1094                                                      GtkCellRenderer       *cell)
1095 {
1096   HildonTouchSelectorColumn *sel_column = NULL;
1097   GtkTreeViewColumn *view_column = NULL;
1098
1099   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1100   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1101
1102   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1103
1104   gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (view_column), cell);
1105 }
1106
1107 static void
1108 hildon_touch_selector_column_cell_layout_reorder (GtkCellLayout         *cell_layout,
1109                                             GtkCellRenderer       *cell,
1110                                             gint                   position)
1111 {
1112   HildonTouchSelectorColumn *sel_column = NULL;
1113   GtkTreeViewColumn *view_column = NULL;
1114
1115   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1116   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1117
1118   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1119
1120   gtk_cell_layout_reorder (GTK_CELL_LAYOUT(view_column), cell, position);
1121 }
1122
1123 static GList*
1124 hildon_touch_selector_column_cell_layout_get_cells (GtkCellLayout         *cell_layout)
1125 {
1126   HildonTouchSelectorColumn *sel_column = NULL;
1127   GtkTreeViewColumn *view_column = NULL;
1128
1129   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout), NULL);
1130   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1131
1132   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1133
1134   return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(view_column));
1135 }
1136
1137 /* ------------------------------ PUBLIC METHODS ---------------------------- */
1138
1139 /**
1140  * hildon_touch_selector_new:
1141  *
1142  * Creates a new empty #HildonTouchSelector.
1143  *
1144  * Returns: a new #HildonTouchSelector.
1145  *
1146  * Since: 2.2
1147  **/
1148 GtkWidget *
1149 hildon_touch_selector_new (void)
1150 {
1151   return g_object_new (HILDON_TYPE_TOUCH_SELECTOR, NULL);
1152 }
1153
1154 /**
1155  * hildon_touch_selector_new_text:
1156  *
1157  * Creates a #HildonTouchSelector with a single text column that
1158  * can be populated conveniently through hildon_touch_selector_append_text(),
1159  * hildon_touch_selector_prepend_text(), hildon_touch_selector_insert_text().
1160  *
1161  * Returns: A new #HildonTouchSelector
1162  *
1163  * Since: 2.2
1164  **/
1165 GtkWidget *
1166 hildon_touch_selector_new_text (void)
1167 {
1168   GtkWidget *selector;
1169   GtkListStore *store;
1170   HildonTouchSelectorColumn *column = NULL;
1171
1172   selector = hildon_touch_selector_new ();
1173   store = gtk_list_store_new (1, G_TYPE_STRING);
1174
1175   column = hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
1176                                                      GTK_TREE_MODEL (store), TRUE);
1177
1178   hildon_touch_selector_column_set_text_column (column, 0);
1179
1180   return selector;
1181 }
1182
1183 /**
1184  * hildon_touch_selector_append_text:
1185  * @selector: A #HildonTouchSelector.
1186  * @text: a non %NULL text string.
1187  *
1188  * Appends a new entry in a #HildonTouchSelector created with
1189  * hildon_touch_selector_new_text().
1190  *
1191  * Since: 2.2
1192  **/
1193 void
1194 hildon_touch_selector_append_text (HildonTouchSelector * selector,
1195                                    const gchar * text)
1196 {
1197   GtkTreeIter iter;
1198   GtkTreeModel *model;
1199
1200   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1201   g_return_if_fail (text != NULL);
1202
1203   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
1204
1205   g_return_if_fail (GTK_IS_LIST_STORE (model));
1206
1207   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1208   gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, text, -1);
1209 }
1210
1211 /**
1212  * hildon_touch_selector_prepend_text:
1213  * @selector: A #HildonTouchSelector.
1214  * @text: a non %NULL text string.
1215  *
1216  * Prepends a new entry in a #HildonTouchSelector created with
1217  * hildon_touch_selector_new_text().
1218  *
1219  * Since: 2.2
1220  **/
1221 void
1222 hildon_touch_selector_prepend_text (HildonTouchSelector * selector,
1223                                     const gchar * text)
1224 {
1225   GtkTreeIter iter;
1226   GtkTreeModel *model;
1227
1228   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1229   g_return_if_fail (text != NULL);
1230
1231   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
1232
1233   g_return_if_fail (GTK_IS_LIST_STORE (model));
1234
1235   gtk_list_store_prepend (GTK_LIST_STORE (model), &iter);
1236   gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, text, -1);
1237 }
1238
1239 /**
1240  * hildon_touch_selector_insert_text:
1241  * @selector: a #HildonTouchSelector.
1242  * @position: the position to insert @text.
1243  * @text: A non %NULL text string.
1244  *
1245  * Inserts a new entry in a particular position of a
1246  * #HildonTouchSelector created with hildon_touch_selector_new_text().
1247  *
1248  * Since: 2.2
1249  **/
1250 void
1251 hildon_touch_selector_insert_text (HildonTouchSelector * selector,
1252                                    gint position, const gchar * text)
1253 {
1254   GtkTreeIter iter;
1255   GtkTreeModel *model;
1256
1257   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1258   g_return_if_fail (text != NULL);
1259   g_return_if_fail (position >= 0);
1260
1261   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
1262
1263   g_return_if_fail (GTK_IS_LIST_STORE (model));
1264
1265   gtk_list_store_insert (GTK_LIST_STORE (model), &iter, position);
1266   gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, text, -1);
1267 }
1268
1269 /**
1270  * hildon_touch_selector_append_column
1271  * @selector: a #HildonTouchSelector
1272  * @model: the #GtkTreeModel with the data of the column
1273  * @cell_renderer: The #GtkCellRenderer where to draw each row contents.
1274  * @Varargs: a %NULL-terminated pair of attributes and column numbers.
1275  *
1276  * This functions adds a new column to the widget, whose data will
1277  * be obtained from the model. Only widgets added this way should used on
1278  * the selection logic, the print function, the #HildonTouchSelector::changed
1279  * signal, etc. Internally, a #GtkTreeView will be added to the widget, using
1280  * @model as the data source.
1281  *
1282  * You can optionally pass a #GtkCellRenderer in @cell_renderer,
1283  * together with a %NULL-terminated list of pairs property/value, in
1284  * the same way you would use gtk_tree_view_column_set_attributes().
1285  * This will pack @cell_renderer at the start of the column, expanded
1286  * by default.  If you prefer not to add it this way, you can simply
1287  * pass %NULL to @cell_renderer and use the #GtkCellLayout interface
1288  * on the returned #HildonTouchSelectorColumn to set your
1289  * renderers. Please note that the returned #HildonTouchSelectorColumn
1290  * is owned by @selector, you shouldn't unref it after setting it
1291  * up.
1292  *
1293  * Initially, the returned #HildonTouchSelectorColumn will have its
1294  * #HildonTouchSelectorColumn:text-column property unset. You should set
1295  * it to a column in @model with type %G_TYPE_STRING. See
1296  * hildon_touch_selector_column_set_text_column().
1297  *
1298  * This method could change the current #HildonTouchSelector:hildon-ui-mode.
1299  * %HILDON_UI_MODE_NORMAL is only allowed with one column, so if the selector
1300  * is in this mode, and a additional column is added,
1301  * #HildonTouchSelector:hildon-ui-mode will change to %HILDON_UI_MODE_EDIT.
1302  *
1303  * Returns: the new column added added, %NULL otherwise.
1304  *
1305  * Since: 2.2
1306  **/
1307
1308 HildonTouchSelectorColumn*
1309 hildon_touch_selector_append_column (HildonTouchSelector * selector,
1310                                      GtkTreeModel * model,
1311                                      GtkCellRenderer * cell_renderer, ...)
1312 {
1313   va_list args;
1314   HildonTouchSelectorColumn *new_column = NULL;
1315   gboolean emit_changed = FALSE;
1316   gint colnum;
1317
1318   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1319   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
1320
1321   if (model != NULL) {
1322
1323     va_start (args, cell_renderer);
1324     new_column = _create_new_column (selector, model, &emit_changed, cell_renderer, args);
1325     va_end (args);
1326
1327     selector->priv->columns = g_slist_append (selector->priv->columns,
1328                                               new_column);
1329     gtk_box_pack_start (GTK_BOX (selector->priv->hbox),
1330                         new_column->priv->panarea,
1331                         TRUE, TRUE, 6);
1332
1333     gtk_widget_show_all (new_column->priv->panarea);
1334
1335     if (selector->priv->initial_scroll) {
1336       _hildon_touch_selector_center_on_selected_items (selector, new_column);
1337     }
1338
1339   } else {
1340     return NULL;
1341   }
1342
1343   g_signal_emit (selector, hildon_touch_selector_signals[COLUMNS_CHANGED], 0);
1344   if (emit_changed) {
1345     colnum = g_slist_length (selector->priv->columns);
1346     hildon_touch_selector_emit_value_changed (selector, colnum);
1347   }
1348
1349   hildon_touch_selector_check_ui_mode_coherence (selector);
1350
1351   return new_column;
1352 }
1353
1354 /**
1355  * hildon_touch_selector_append_text_column
1356  * @selector: a #HildonTouchSelector
1357  * @model: a #GtkTreeModel with data for the column
1358  * @center: whether to center the text on the column
1359  *
1360  * Equivalent to hildon_touch_selector_append_column(), but using a
1361  * default text cell renderer. This is the most common use case of the
1362  * widget.
1363  *
1364  * Returns: the new column added, NULL otherwise.
1365  *
1366  * Since: 2.2
1367  **/
1368 HildonTouchSelectorColumn*
1369 hildon_touch_selector_append_text_column (HildonTouchSelector * selector,
1370                                           GtkTreeModel * model, gboolean center)
1371 {
1372   gfloat xalign = center ? 0.5 : 0.0;
1373   GtkCellRenderer *renderer;
1374
1375   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1376   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
1377
1378   renderer = gtk_cell_renderer_text_new ();
1379
1380   g_object_set (renderer,
1381                 "width", 1,
1382                 "xalign", xalign,
1383                 NULL);
1384
1385   return hildon_touch_selector_append_column (selector, model, renderer,
1386                                               "text", 0, NULL);
1387 }
1388
1389 /**
1390  * hildon_touch_selector_remove_column:
1391  * @selector: a #HildonTouchSelector
1392  * @column: the position of the column to be removed
1393  *
1394  * Removes a column from @selector.
1395  *
1396  * Returns: %TRUE if the column was removed, %FALSE otherwise
1397  *
1398  * Since: 2.2
1399  **/
1400 gboolean
1401 hildon_touch_selector_remove_column (HildonTouchSelector * selector, gint column)
1402 {
1403   HildonTouchSelectorColumn *current_column = NULL;
1404   HildonTouchSelectorPrivate *priv;
1405
1406   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
1407   g_return_val_if_fail (column <
1408                         hildon_touch_selector_get_num_columns (selector), FALSE);
1409
1410   priv = HILDON_TOUCH_SELECTOR_GET_PRIVATE (selector);
1411   current_column = g_slist_nth_data (priv->columns, column);
1412
1413   gtk_container_remove (GTK_CONTAINER (priv->hbox), current_column->priv->panarea);
1414   priv->columns = g_slist_remove (priv->columns, current_column);
1415   g_object_unref (current_column);
1416
1417   g_signal_emit (selector, hildon_touch_selector_signals[COLUMNS_CHANGED], 0);
1418
1419   return TRUE;
1420 }
1421
1422 /**
1423  * hildon_touch_selector_set_column_attributes:
1424  * @selector: a #HildonTouchSelector
1425  * @num_column: the number of the column whose attributes we're setting
1426  * @cell_renderer: the #GtkCellRendere we're setting the attributes of
1427  * @Varargs: A %NULL-terminated list of attributes.
1428  *
1429  * Sets the attributes for the given column. The attributes must be given
1430  * in attribute/column pairs, just like in gtk_tree_view_column_set_attributes().
1431  * All existing attributes are removed and replaced with the new ones.
1432  *
1433  * Deprecated: #HildonTouchSelectorColumn implements #GtkCellLayout, use this
1434  *             interface instead. See
1435  *             hildon_touch_selector_get_column().
1436  *
1437  * Since: 2.2
1438  **/
1439 void
1440 hildon_touch_selector_set_column_attributes (HildonTouchSelector * selector,
1441                                              gint num_column,
1442                                              GtkCellRenderer * cell_renderer,
1443                                              ...)
1444 {
1445   va_list args;
1446   GtkTreeViewColumn *tree_column = NULL;
1447   HildonTouchSelectorColumn *current_column = NULL;
1448   gchar *attribute = NULL;
1449   gint value = 0;
1450
1451   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1452   g_return_if_fail (num_column <
1453                     hildon_touch_selector_get_num_columns (selector));
1454
1455   current_column = g_slist_nth_data (selector->priv->columns, num_column);
1456
1457   tree_column = gtk_tree_view_get_column (current_column->priv->tree_view, 0);
1458   gtk_tree_view_remove_column (current_column->priv->tree_view, tree_column);
1459
1460   tree_column = gtk_tree_view_column_new ();
1461   gtk_tree_view_column_pack_start (tree_column, cell_renderer, TRUE);
1462
1463   va_start (args, cell_renderer);
1464   attribute = va_arg (args, gchar *);
1465
1466   gtk_tree_view_column_clear_attributes (tree_column, cell_renderer);
1467
1468   while (attribute != NULL) {
1469     value = va_arg (args, gint);
1470     gtk_tree_view_column_add_attribute (tree_column, cell_renderer,
1471                                         attribute, value);
1472     attribute = va_arg (args, gchar *);
1473   }
1474
1475   va_end (args);
1476
1477   gtk_tree_view_append_column (current_column->priv->tree_view, tree_column);
1478 }
1479
1480 /**
1481  * hildon_touch_selector_get_num_columns:
1482  * @selector: a #HildonTouchSelector
1483  *
1484  * Gets the number of columns in the #HildonTouchSelector.
1485  *
1486  * Returns: the number of columns in @selector.
1487  *
1488  * Since: 2.2
1489  **/
1490 gint
1491 hildon_touch_selector_get_num_columns (HildonTouchSelector * selector)
1492 {
1493   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), -1);
1494
1495   return g_slist_length (selector->priv->columns);
1496 }
1497
1498 /**
1499  * hildon_touch_selector_get_column_selection_mode:
1500  * @selector: a #HildonTouchSelector
1501  *
1502  * Gets the selection mode of @selector.
1503  *
1504  * Returns: one of #HildonTouchSelectorSelectionMode
1505  *
1506  * Since: 2.2
1507  **/
1508 HildonTouchSelectorSelectionMode
1509 hildon_touch_selector_get_column_selection_mode (HildonTouchSelector * selector)
1510 {
1511   HildonTouchSelectorSelectionMode result =
1512     HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
1513   GtkSelectionMode treeview_mode = GTK_SELECTION_BROWSE;
1514   HildonTouchSelectorColumn *column = NULL;
1515   GtkTreeSelection *selection = NULL;
1516
1517   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), result);
1518   g_return_val_if_fail (hildon_touch_selector_get_num_columns (selector) > 0,
1519                         result);
1520
1521   column = HILDON_TOUCH_SELECTOR_COLUMN (selector->priv->columns->data);
1522
1523   selection = gtk_tree_view_get_selection (column->priv->tree_view);
1524   treeview_mode = gtk_tree_selection_get_mode (selection);
1525
1526
1527   if (treeview_mode == GTK_SELECTION_MULTIPLE) {
1528     result = HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE;
1529   } else {
1530     result = HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
1531   }
1532
1533   return result;
1534 }
1535
1536 /**
1537  * hildon_touch_selector_set_column_selection_mode:
1538  * @selector: a #HildonTouchSelector
1539  * @mode: the #HildonTouchSelectorMode for @selector
1540  *
1541  * Sets the selection mode for @selector. See #HildonTouchSelectorSelectionMode.
1542  *
1543  * The new @mode will be set, but take into into account that the
1544  * #HildonTouchSelectorSelectionMode is ignored if the @selector
1545  * #HildonTouchSelector:hildon-ui-mode property is set to %HILDON_UI_MODE_NORMAL
1546  *
1547  * Since: 2.2
1548  **/
1549 void
1550 hildon_touch_selector_set_column_selection_mode (HildonTouchSelector * selector,
1551                                                  HildonTouchSelectorSelectionMode mode)
1552 {
1553   GtkTreeView *tv = NULL;
1554   HildonTouchSelectorColumn *column = NULL;
1555   GtkTreeSelection *selection = NULL;
1556   GtkSelectionMode treeview_mode = GTK_SELECTION_MULTIPLE;
1557   GtkTreeIter iter;
1558   HildonTouchSelectorSelectionMode current_mode;
1559
1560   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1561   g_return_if_fail (hildon_touch_selector_get_num_columns (selector) > 0);
1562
1563   current_mode = hildon_touch_selector_get_column_selection_mode (selector);
1564
1565   if (current_mode == mode) {
1566     return;
1567   }
1568
1569   column = HILDON_TOUCH_SELECTOR_COLUMN ((g_slist_nth (selector->priv->columns, 0))->data);
1570   tv = column->priv->tree_view;
1571
1572   if (tv) {
1573     switch (mode) {
1574     case HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE:
1575       treeview_mode = GTK_SELECTION_BROWSE;
1576       break;
1577     case HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE:
1578       treeview_mode = GTK_SELECTION_MULTIPLE;
1579       break;
1580     }
1581
1582     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
1583     gtk_tree_selection_set_mode (selection, treeview_mode);
1584
1585     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
1586     gtk_tree_model_get_iter_first (column->priv->model, &iter);
1587     gtk_tree_selection_unselect_all (selection);
1588     gtk_tree_selection_select_iter (selection, &iter);
1589
1590     /* the column changed was the first one */
1591     hildon_touch_selector_emit_value_changed (selector, 0);
1592   }
1593
1594 }
1595
1596 /**
1597  * hildon_touch_selector_set_print_func:
1598  * @selector: a #HildonTouchSelector
1599  * @func: a #HildonTouchSelectorPrintFunc function
1600  *
1601  * Sets the function to be used by hildon_touch_selector_get_current_text().
1602  * See hildon_touch_selector_set_print_func_full().
1603  *
1604  * Since: 2.2
1605  **/
1606 void
1607 hildon_touch_selector_set_print_func (HildonTouchSelector * selector,
1608                                       HildonTouchSelectorPrintFunc func)
1609 {
1610   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1611
1612   hildon_touch_selector_set_print_func_full (selector, func, NULL, NULL);
1613 }
1614
1615 /**
1616  * hildon_touch_selector_set_print_func_full:
1617  * @selector: a #HildonTouchSelector
1618  * @func: a #HildonTouchSelectorPrintFunc function
1619  * @user_data: a pointer to user data or %NULL
1620  * @destroy_func: a callback for freeing the user data or %NULL
1621  *
1622  * Sets the function to be used by hildon_touch_selector_get_current_text()
1623  * to produce a text representation of the currently selected items in @selector.
1624  * The default function will return a concatenation of comma separated items
1625  * selected in each column in @selector. Use this to override this method if you
1626  * need a particular representation for your application.
1627  *
1628  * Since: 2.2
1629  **/
1630 void
1631 hildon_touch_selector_set_print_func_full (HildonTouchSelector          *selector,
1632                                            HildonTouchSelectorPrintFunc  func,
1633                                            gpointer                      user_data,
1634                                            GDestroyNotify                destroy_func)
1635 {
1636   gpointer       old_user_data;
1637   GDestroyNotify old_destroy_func;
1638
1639   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1640
1641   old_user_data = selector->priv->print_user_data;
1642   old_destroy_func = selector->priv->print_destroy_func;
1643
1644   selector->priv->print_func = func;
1645   selector->priv->print_user_data = user_data;
1646   selector->priv->print_destroy_func = destroy_func;
1647
1648   if (old_destroy_func && old_user_data != user_data)
1649     (*old_destroy_func) (old_user_data);
1650 }
1651
1652 /**
1653  * hildon_touch_selector_get_print_func:
1654  * @selector: a #HildonTouchSelector
1655  *
1656  * Gets the #HildonTouchSelectorPrintFunc currently used. See
1657  * hildon_touch_selector_set_print_func().
1658  *
1659  * Returns: a #HildonTouchSelectorPrintFunc or %NULL if the default
1660  * one is currently used.
1661  **/
1662 HildonTouchSelectorPrintFunc
1663 hildon_touch_selector_get_print_func (HildonTouchSelector * selector)
1664 {
1665   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1666
1667   return selector->priv->print_func;
1668 }
1669
1670 /**
1671  * hildon_touch_selector_set_active:
1672  * @selector: a #HildonTouchSelector
1673  * @column: column number
1674  * @index: the index of the item to select, or -1 to have no active item
1675  *
1676  * Sets the active item of the #HildonTouchSelector to @index. The
1677  * column number is taken from @column.
1678  *
1679  * @selector must be in %HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE
1680  *
1681  * Since: 2.2
1682  **/
1683 void
1684 hildon_touch_selector_set_active                (HildonTouchSelector *selector,
1685                                                  gint                 column,
1686                                                  gint                 index)
1687 {
1688   GtkTreeSelection *selection = NULL;
1689   HildonTouchSelectorColumn *current_column = NULL;
1690   HildonTouchSelectorSelectionMode mode;
1691   GtkTreePath *path;
1692
1693   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1694   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
1695   mode = hildon_touch_selector_get_column_selection_mode (selector);
1696   g_return_if_fail (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE);
1697
1698   current_column = g_slist_nth_data (selector->priv->columns, column);
1699
1700   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
1701   path = gtk_tree_path_new_from_indices (index, -1);
1702   gtk_tree_selection_unselect_all (selection);
1703   if (index != -1)
1704     gtk_tree_selection_select_path (selection, path);
1705
1706   hildon_touch_selector_emit_value_changed (selector, column);
1707
1708   gtk_tree_path_free (path);
1709 }
1710
1711 /**
1712  * hildon_touch_selector_get_active:
1713  * @selector: a #HildonTouchSelector
1714  * @column: column number
1715  *
1716  * Returns the index of the currently active item in column number
1717  * @column, or -1 if there's no active item.
1718  *
1719  * @selector must be in %HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE
1720  *
1721  * Returns: an integer which is the index of the currently active
1722  * item, or -1 if there's no active item.
1723  *
1724  * Since: 2.2
1725  **/
1726 gint
1727 hildon_touch_selector_get_active                (HildonTouchSelector *selector,
1728                                                  gint                 column)
1729 {
1730   GtkTreeSelection *selection = NULL;
1731   HildonTouchSelectorColumn *current_column = NULL;
1732   HildonTouchSelectorSelectionMode mode;
1733   GtkTreeModel *model;
1734   GtkTreeIter iter;
1735   GtkTreePath *path;
1736   gint index;
1737
1738   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), -1);
1739   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector), -1);
1740   mode = hildon_touch_selector_get_column_selection_mode (selector);
1741   g_return_val_if_fail (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE, -1);
1742
1743   current_column = g_slist_nth_data (selector->priv->columns, column);
1744
1745   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
1746   g_return_val_if_fail (gtk_tree_selection_get_selected (selection, NULL, &iter), -1);
1747
1748   model = gtk_tree_view_get_model (GTK_TREE_VIEW (current_column->priv->tree_view));
1749   path = gtk_tree_model_get_path (model, &iter);
1750   index = (gtk_tree_path_get_indices (path))[0];
1751
1752   gtk_tree_path_free (path);
1753
1754   return index;
1755 }
1756
1757 /**
1758  * hildon_touch_selector_get_selected:
1759  * @selector: a #HildonTouchSelector
1760  * @column: the column number we want to get the element
1761  * @iter: #GtkTreeIter currently selected
1762  *
1763  * Sets @iter to the currently selected node on the nth-column, if selection is
1764  * set to %HILDON_TOUCH_SELECTOR_SINGLE or %HILDON_TOUCH_SELECTOR_MULTIPLE with
1765  * a column different that the first one. @iter may be %NULL if you just want to
1766  * test if selection has any selected items.
1767  *
1768  * This function will not work if selection is in
1769  * %HILDON_TOUCH_SELECTOR_MULTIPLE mode and the column is the first one.
1770  *
1771  * See gtk_tree_selection_get_selected() for more information.
1772  *
1773  * Returns: %TRUE if @iter was correctly set, %FALSE otherwise
1774  *
1775  * Since: 2.2
1776  **/
1777 gboolean
1778 hildon_touch_selector_get_selected (HildonTouchSelector * selector,
1779                                     gint column, GtkTreeIter * iter)
1780 {
1781   GtkTreeSelection *selection = NULL;
1782   HildonTouchSelectorColumn *current_column = NULL;
1783   HildonTouchSelectorSelectionMode mode;
1784
1785   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
1786   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
1787                         FALSE);
1788   mode = hildon_touch_selector_get_column_selection_mode (selector);
1789   g_return_val_if_fail
1790     ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE) ||
1791      ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)&&(column>0)),
1792      FALSE);
1793
1794   current_column = g_slist_nth_data (selector->priv->columns, column);
1795
1796   selection =
1797     gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
1798
1799   return gtk_tree_selection_get_selected (selection, NULL, iter);
1800 }
1801
1802 /**
1803  * hildon_touch_selector_select_iter
1804  * @selector: a #HildonTouchSelector
1805  * @column:   the column to selects
1806  * @iter:     the #GtkTreeIter to be selected
1807  * @scroll_to: whether to smoothly scroll to the item
1808  *
1809  * Sets the currently selected item in the column @column to the one pointed by @iter,
1810  * optionally smoothly scrolling to it.
1811  *
1812  * Since: 2.2
1813  **/
1814 void
1815 hildon_touch_selector_select_iter (HildonTouchSelector * selector,
1816                                    gint column, GtkTreeIter * iter,
1817                                    gboolean scroll_to)
1818 {
1819   GtkTreePath *path;
1820   GtkTreeModel *model;
1821   HildonTouchSelectorColumn *current_column = NULL;
1822   GtkTreeView *tv = NULL;
1823   GtkTreeSelection *selection = NULL;
1824
1825   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1826   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
1827
1828   current_column = g_slist_nth_data (selector->priv->columns, column);
1829
1830   tv = current_column->priv->tree_view;
1831   selection = gtk_tree_view_get_selection (tv);
1832   model = gtk_tree_view_get_model (tv);
1833   path = gtk_tree_model_get_path (model, iter);
1834
1835   gtk_tree_selection_select_iter (selection, iter);
1836
1837   if (scroll_to) {
1838     hildon_touch_selector_scroll_to (current_column, tv, path);
1839   }
1840
1841   hildon_touch_selector_emit_value_changed (selector, column);
1842
1843   gtk_tree_path_free (path);
1844 }
1845
1846 /**
1847  * hildon_touch_selector_unselect_iter
1848  * @selector: a #HildonTouchSelector
1849  * @column:   the column to unselects from
1850  * @iter:     the #GtkTreeIter to be unselected
1851  *
1852  * Unselect the item pointed by @iter in the column @column
1853  *
1854  * Since: 2.2
1855  **/
1856
1857 void hildon_touch_selector_unselect_iter (HildonTouchSelector * selector,
1858                                           gint column,
1859                                           GtkTreeIter * iter)
1860 {
1861   HildonTouchSelectorColumn *current_column = NULL;
1862   GtkTreeSelection *selection = NULL;
1863
1864   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1865   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
1866
1867   current_column = g_slist_nth_data (selector->priv->columns, column);
1868   selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
1869   gtk_tree_selection_unselect_iter (selection, iter);
1870
1871   hildon_touch_selector_emit_value_changed (selector, column);
1872 }
1873
1874 /**
1875  * hildon_touch_selector_unselect_all:
1876  * @selector: a #HildonTouchSelector
1877  * @column: the position of the column to get the selected rows from
1878  *
1879  * Unselects all the selected items in the column @column.
1880  *
1881  * Since: 2.2
1882  **/
1883 void
1884 hildon_touch_selector_unselect_all (HildonTouchSelector * selector,
1885                                     gint column)
1886 {
1887   HildonTouchSelectorColumn *current_column = NULL;
1888   GtkTreeSelection *selection = NULL;
1889
1890   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1891   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
1892
1893   current_column = g_slist_nth_data (selector->priv->columns, column);
1894   selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
1895   gtk_tree_selection_unselect_all (selection);
1896
1897   hildon_touch_selector_emit_value_changed (selector, column);
1898 }
1899
1900 /**
1901  * hildon_touch_selector_get_selected_rows:
1902  * @selector: a #HildonTouchSelector
1903  * @column: the position of the column to get the selected rows from
1904  *
1905  * Creates a list of #GtkTreePath<!-- -->s of all selected rows in a column. Additionally,
1906  * if you to plan to modify the model after calling this function, you may
1907  * want to convert the returned list into a list of GtkTreeRowReferences. To do this,
1908  * you can use gtk_tree_row_reference_new().
1909  *
1910  * See gtk_tree_selection_get_selected_rows() for more information.
1911  *
1912  * Returns: A new #GList containing a #GtkTreePath for each selected row in the column @column.
1913  *
1914  * Since: 2.2
1915  **/
1916 GList *
1917 hildon_touch_selector_get_selected_rows (HildonTouchSelector * selector,
1918                                          gint column)
1919 {
1920   GList *result = NULL;
1921   HildonTouchSelectorColumn *current_column = NULL;
1922   GtkTreeSelection *selection = NULL;
1923
1924   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1925   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
1926                         NULL);
1927
1928   current_column = g_slist_nth_data (selector->priv->columns, column);
1929   selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
1930
1931   result = gtk_tree_selection_get_selected_rows (selection, NULL);
1932
1933   return result;
1934 }
1935
1936 /**
1937  * hildon_touch_selector_get_model:
1938  * @selector: a #HildonTouchSelector
1939  * @column: the position of the column in @selector
1940  *
1941  * Gets the model of a column of @selector.
1942  *
1943  * Returns: the #GtkTreeModel for the column @column of @selector.
1944  *
1945  * Since: 2.2
1946  **/
1947 GtkTreeModel *
1948 hildon_touch_selector_get_model (HildonTouchSelector * selector, gint column)
1949 {
1950   HildonTouchSelectorColumn *current_column = NULL;
1951
1952   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1953   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
1954                         NULL);
1955
1956   current_column = g_slist_nth_data (selector->priv->columns, column);
1957
1958   return current_column->priv->model;
1959 }
1960
1961 static void
1962 on_row_changed (GtkTreeModel *model,
1963                 GtkTreePath *path,
1964                 GtkTreeIter *iter,
1965                 gpointer userdata)
1966 {
1967   HildonTouchSelector *selector;
1968   HildonTouchSelectorColumn *current_column;
1969
1970   gint column = 0;
1971   GSList *col;
1972
1973   selector = HILDON_TOUCH_SELECTOR (userdata);
1974
1975   for (col = selector->priv->columns; col != NULL; col = col->next) {
1976     current_column = HILDON_TOUCH_SELECTOR_COLUMN (col->data);
1977     if (current_column->priv->model == model &&
1978         gtk_tree_selection_path_is_selected (gtk_tree_view_get_selection (current_column->priv->tree_view),
1979                                              path)) {
1980       hildon_touch_selector_emit_value_changed (selector, column);
1981     }
1982     column ++;
1983   }
1984 }
1985
1986 static void
1987 _hildon_touch_selector_set_model (HildonTouchSelector * selector,
1988                                   gint column, GtkTreeModel * model)
1989 {
1990   HildonTouchSelectorColumn *current_column = NULL;
1991
1992   current_column =
1993     HILDON_TOUCH_SELECTOR_COLUMN (g_slist_nth_data (selector->priv->columns, column));
1994
1995   if (current_column->priv->model) {
1996     g_signal_handlers_disconnect_by_func (current_column->priv->model,
1997                                           on_row_changed, selector);
1998   }
1999   current_column->priv->model = model;
2000   gtk_tree_view_set_model (current_column->priv->tree_view,
2001                            current_column->priv->model);
2002   g_signal_connect (model, "row-changed",
2003                     G_CALLBACK (on_row_changed), selector);
2004 }
2005
2006 /**
2007  * hildon_touch_selector_set_model:
2008  * @selector: a #HildonTouchSelector
2009  * @column: the position of the column to set the model to
2010  * @model: a #GtkTreeModel
2011  *
2012  * Sets the #GtkTreeModel for a particular column in @model.
2013  *
2014  * Since: 2.2
2015  **/
2016 void
2017 hildon_touch_selector_set_model (HildonTouchSelector * selector,
2018                                  gint column, GtkTreeModel * model)
2019 {
2020   g_return_if_fail (HILDON_TOUCH_SELECTOR (selector));
2021   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
2022
2023   HILDON_TOUCH_SELECTOR_GET_CLASS (selector)->set_model (selector, column, model);
2024 }
2025
2026 /**
2027  * hildon_touch_selector_get_current_text:
2028  * @selector: a #HildonTouchSelector
2029  *
2030  * Returns a string representing the currently selected items for
2031  * each column of @selector. See hildon_touch_selector_set_print_func().
2032  *
2033  * Returns: a newly allocated string.
2034  *
2035  * Since: 2.2
2036  **/
2037 gchar *
2038 hildon_touch_selector_get_current_text (HildonTouchSelector * selector)
2039 {
2040   gchar *result = NULL;
2041   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
2042
2043   if (selector->priv->print_func) {
2044     result = (*selector->priv->print_func) (selector, selector->priv->print_user_data);
2045   } else {
2046     result = _default_print_func (selector, NULL);
2047   }
2048
2049   return result;
2050 }
2051
2052 static void
2053 search_nearest_element (HildonPannableArea *panarea,
2054                         GtkTreeView *tv,
2055                         GList *selected_rows,
2056                         GtkTreePath **nearest_path)
2057 {
2058   GtkAdjustment *adj = NULL;
2059   gdouble target_value = 0;
2060   GdkRectangle rect;
2061   GList *iter = NULL;
2062   GtkTreePath *path = NULL;
2063   gint y = -1;
2064   gdouble nearest_distance = -1;
2065   gdouble current_distance = -1;
2066   GtkTreePath *result_path = NULL;
2067
2068   g_assert (nearest_path != NULL);
2069
2070   if (selected_rows == NULL) {
2071     *nearest_path = NULL;
2072     return;
2073   }
2074
2075   adj = hildon_pannable_area_get_vadjustment (panarea);
2076   g_return_if_fail (adj != NULL);
2077
2078   /* we add this in order to check the nearest to the center of
2079      the visible area */
2080   target_value = gtk_adjustment_get_value (adj) + adj->page_size/2;
2081
2082   path = result_path = selected_rows->data;
2083   gtk_tree_view_get_background_area (tv, path, NULL, &rect);
2084   gtk_tree_view_convert_bin_window_to_tree_coords (tv, 0, rect.y, NULL, &y);
2085   nearest_distance = abs (target_value - y);
2086
2087   for (iter = selected_rows->next; iter; iter = g_list_next (iter)) {
2088     gtk_tree_view_get_background_area (tv, path, NULL, &rect);
2089     gtk_tree_view_convert_bin_window_to_tree_coords (tv, 0, rect.y, NULL, &y);
2090     current_distance = abs (target_value - y);
2091     if (current_distance < nearest_distance) {
2092       nearest_distance = current_distance;
2093       result_path = path;
2094     }
2095   }
2096
2097   *nearest_path = result_path;
2098 }
2099
2100 static gboolean
2101 on_realize_cb                                  (GtkWidget *widget,
2102                                                 gpointer data)
2103 {
2104   HildonTouchSelectorColumn *column = NULL;
2105   GdkRectangle rect;
2106   gint y;
2107
2108   column = HILDON_TOUCH_SELECTOR_COLUMN (data);
2109
2110   if (column->priv->initial_path) {
2111     gtk_tree_view_get_background_area (GTK_TREE_VIEW (column->priv->tree_view),
2112                                        column->priv->initial_path, NULL, &rect);
2113     gtk_tree_view_convert_bin_window_to_tree_coords
2114       (GTK_TREE_VIEW (column->priv->tree_view),
2115        0, rect.y, NULL, &y);
2116
2117     hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA (column->priv->panarea),
2118                                     -1, y);
2119
2120     gtk_tree_path_free (column->priv->initial_path);
2121
2122     column->priv->initial_path = NULL;
2123
2124   }
2125
2126   g_signal_handler_disconnect (column->priv->panarea,
2127                                column->priv->realize_handler);
2128
2129   return FALSE;
2130 }
2131
2132 static void
2133 hildon_touch_selector_scroll_to (HildonTouchSelectorColumn *column,
2134                                  GtkTreeView *tv,
2135                                  GtkTreePath *path)
2136 {
2137   if (GTK_WIDGET_REALIZED (column->priv->panarea)) {
2138     GdkRectangle rect;
2139     gint y;
2140
2141     gtk_tree_view_get_background_area (tv,
2142                                        path, NULL, &rect);
2143     gtk_tree_view_convert_bin_window_to_tree_coords (tv,
2144                                                      0, rect.y, NULL, &y);
2145
2146     hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA
2147                                     (column->priv->panarea), -1, y);
2148   } else {
2149     if (column->priv->realize_handler != 0) {
2150
2151       if (column->priv->initial_path) {
2152         gtk_tree_path_free (column->priv->initial_path);
2153         column->priv->initial_path = NULL;
2154       }
2155
2156       g_signal_handler_disconnect (column->priv->panarea,
2157                                    column->priv->realize_handler);
2158       column->priv->realize_handler = 0;
2159     }
2160
2161     column->priv->initial_path = gtk_tree_path_copy (path);
2162     column->priv->realize_handler =
2163       g_signal_connect_after (G_OBJECT (column->priv->panarea), "realize",
2164                               G_CALLBACK (on_realize_cb),
2165                               column);
2166   }
2167 }
2168
2169 /**
2170  *
2171  * Center on the selected item of a concrete column
2172  *
2173  * Returns: TRUE if was able to do that
2174  *          FALSE otherwise
2175  */
2176 static gboolean
2177 _hildon_touch_selector_center_on_selected_items (HildonTouchSelector *selector,
2178                                                  HildonTouchSelectorColumn *column)
2179 {
2180   GtkTreePath *path = NULL;
2181   GList *selected_rows = NULL;
2182   gint num_column = -1;
2183
2184   num_column = g_slist_index (selector->priv->columns, column);
2185
2186   selected_rows = hildon_touch_selector_get_selected_rows (selector, num_column);
2187   if (selected_rows) {
2188     search_nearest_element (HILDON_PANNABLE_AREA (column->priv->panarea),
2189                              GTK_TREE_VIEW (column->priv->tree_view),
2190                              selected_rows,
2191                              &path);
2192
2193     if (path != NULL) {
2194       hildon_touch_selector_scroll_to (column,
2195                                        GTK_TREE_VIEW (column->priv->tree_view),
2196                                        path);
2197     } else {
2198       return FALSE;
2199     }
2200
2201     g_list_foreach (selected_rows, (GFunc) (gtk_tree_path_free), NULL);
2202     g_list_free (selected_rows);
2203   }
2204
2205   return TRUE;
2206 }
2207
2208 static gboolean
2209 _hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
2210 {
2211   HildonTouchSelectorSelectionMode mode;
2212   gint n_columns;
2213
2214   n_columns = hildon_touch_selector_get_num_columns (selector);
2215   mode = hildon_touch_selector_get_column_selection_mode (selector);
2216
2217   return ((n_columns > 1) || (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE));
2218 }
2219
2220 /**
2221  * hildon_touch_selector_has_multiple_selection:
2222  * @selector: A #HildonTouchSelector
2223  *
2224  * Determines whether @selector is complex enough to actually require an
2225  * extra selection step than only picking an item. This is normally %TRUE
2226  * if @selector has multiple columns, multiple selection, or when it is a
2227  * more complex widget, like #HildonTouchSelectorEntry.
2228  *
2229  * This information is useful for widgets containing a #HildonTouchSelector,
2230  * like #HildonPickerDialog, that could need a "Done" button, in case that
2231  * its internal #HildonTouchSelector has multiple columns, for instance.
2232  *
2233  * Returns: %TRUE if @selector requires multiple selection steps.
2234  *
2235  * Since: 2.2
2236  **/
2237 gboolean
2238 hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
2239 {
2240   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
2241
2242   return HILDON_TOUCH_SELECTOR_GET_CLASS (selector)->has_multiple_selection (selector);
2243 }
2244
2245
2246 /**
2247  * hildon_touch_selector_get_column:
2248  * @selector: A #HildonTouchSelector
2249  * @column: a column number
2250  *
2251  * Use this method to retrieve a #HildonTouchSelectorColumn. Then, you can use
2252  * the #GtkCellLayout interface to set up the layout of the column.
2253  *
2254  * Returns: the @column<!-- -->-th #HildonTouchSelectorColumn in @selector
2255  *
2256  * Since: 2.2
2257  **/
2258 HildonTouchSelectorColumn *
2259 hildon_touch_selector_get_column (HildonTouchSelector * selector,
2260                                   gint column)
2261 {
2262   gint num_columns = -1;
2263
2264   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
2265   num_columns = hildon_touch_selector_get_num_columns (selector);
2266   g_return_val_if_fail (column < num_columns && column >= 0, NULL);
2267
2268   return g_slist_nth_data (selector->priv->columns, column);
2269 }
2270
2271
2272 /**
2273  * hildon_touch_selector_center_on_selected:
2274  * @selector: a #HildonTouchSelector
2275  *
2276  * Ensures all the columns in a #HildonTouchSelector show a selected
2277  * item. If one of the columns is in
2278  * %HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE mode, that column
2279  * will be scrolled to ensure the selected item that is closest to the
2280  * currently visible area is shown.
2281  *
2282  * Since: 2.2
2283  **/
2284 void
2285 hildon_touch_selector_center_on_selected         (HildonTouchSelector *selector)
2286 {
2287   GSList *iter = NULL;
2288
2289   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2290
2291   for (iter = selector->priv->columns; iter; iter = g_slist_next (iter)) {
2292     _hildon_touch_selector_center_on_selected_items (selector,
2293                                                     HILDON_TOUCH_SELECTOR_COLUMN (iter->data));
2294   }
2295 }
2296
2297 /**
2298  * hildon_touch_selector_optimal_size_request
2299  * @selector: a #HildonTouchSelector
2300  * @requisition: a #GtkRequisition
2301  *
2302  * Gets the optimal size request of the touch selector. This function is mostly
2303  * intended for dialog implementations that include a #HildonTouchSelector and
2304  * want to optimize the screen real state, for example, when you want a dialog
2305  * to show as much of the selector, avoiding any extra empty space below the
2306  * selector.
2307  *
2308  * See #HildonPickerDialog implementation for an example.
2309  *
2310  * This function is oriented to be used in the size_request of a dialog or window,
2311  * if you are not sure do not use it.
2312  *
2313  * There is a precondition to this function: Since this function does not
2314  * call the "size_request" method, it can only be used when you know that
2315  * gtk_widget_size_request() has been called since the last time a resize was
2316  * queued.
2317  *
2318  * Since: 2.2
2319  **/
2320 void
2321 hildon_touch_selector_optimal_size_request      (HildonTouchSelector *selector,
2322                                                  GtkRequisition *requisition)
2323 {
2324   GSList *iter = NULL;
2325   gint height = 0;
2326   gint base_height = 0;
2327
2328   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2329
2330   iter = selector->priv->columns;
2331
2332   /* Default optimal values are the current ones */
2333   gtk_widget_get_child_requisition (GTK_WIDGET (selector),
2334                                     requisition);
2335
2336   if (iter == NULL) {
2337     height = requisition->height;
2338   } else {
2339     /* we use the normal requisition as base, as the touch selector can has
2340        extra widgets, not only the columns (ie: HildonTouchSelectorEntry) */
2341     base_height = requisition->height;
2342   }
2343
2344   /* Compute optimal height for the columns */
2345   while (iter) {
2346     HildonTouchSelectorColumn *column;
2347     GtkWidget *child;
2348     GtkRequisition child_requisition = {0};
2349
2350     column = HILDON_TOUCH_SELECTOR_COLUMN (iter->data);
2351     child = GTK_WIDGET (column->priv->tree_view);
2352
2353     gtk_widget_get_child_requisition (child, &child_requisition);
2354
2355     height = MAX (height, child_requisition.height);
2356
2357     iter = g_slist_next (iter);
2358   }
2359
2360   requisition->height = base_height + height;
2361 }
2362
2363
2364
2365 /**
2366  * hildon_touch_selector_get_hildon_ui_mode
2367  * @selector: a #HildonTouchSelector
2368  *
2369  * Gets the current hildon-ui-mode, see #HildonUIMode for more information
2370  *
2371  * Returns: the current hildon-ui-mode
2372  *
2373  * Since: 2.2
2374  **/
2375 HildonUIMode
2376 hildon_touch_selector_get_hildon_ui_mode        (HildonTouchSelector *selector)
2377 {
2378   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), HILDON_UI_MODE_EDIT);
2379
2380   return selector->priv->hildon_ui_mode;
2381 }
2382
2383 /**
2384  * hildon_touch_selector_set_hildon_ui_mode
2385  * @selector: a #HildonTouchSelector
2386  * @mode: a #HildonUIMode
2387  *
2388  * Sets the value of the property #HildonTouchSelector:hildon-ui-mode to be @mode,
2389  * see #HildonUIMode for more information
2390  *
2391  * Note that the %HILDON_UI_MODE_NORMAL can be only used when the selector has
2392  * one column, use the return value to check if the change was effective.
2393  *
2394  * Returns: %TRUE if #HildonTouchSelector:hildon-ui-mode was changed
2395  *          %FALSE otherwise
2396  *
2397  * Since: 2.2
2398  **/
2399 gboolean
2400 hildon_touch_selector_set_hildon_ui_mode        (HildonTouchSelector *selector,
2401                                                  HildonUIMode         mode)
2402 {
2403   gint num = 0;
2404   GSList *iter = NULL;
2405   HildonTouchSelectorColumn *column = NULL;
2406   GtkTreeView *tree_view = NULL;
2407
2408   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
2409   num = hildon_touch_selector_get_num_columns (selector);
2410   g_return_val_if_fail ((mode == HILDON_UI_MODE_EDIT) || (num == 1), FALSE);
2411
2412   if (mode == selector->priv->hildon_ui_mode) {
2413     return FALSE;
2414   }
2415
2416   for (iter = selector->priv->columns; iter; iter = g_slist_next (iter)) {
2417     column = HILDON_TOUCH_SELECTOR_COLUMN (iter->data);
2418     tree_view = column->priv->tree_view;
2419
2420     hildon_tree_view_set_hildon_ui_mode (tree_view, mode);
2421
2422     /* looking at the code of hildon_tree_view_set_hildon_ui_mode, it seems
2423        that it call the unselect_all, but it is required anyway */
2424     if (mode == HILDON_UI_MODE_NORMAL) {
2425       GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
2426
2427       gtk_tree_selection_unselect_all (selection);
2428     }
2429   }
2430
2431   selector->priv->hildon_ui_mode = mode;
2432
2433   return TRUE;
2434 }
2435
2436 /**
2437  * hildon_touch_selector_get_last_activated_row
2438  * @selector: a #HildonTouchSelector
2439  * @column: column number
2440  *
2441  * Gets a #GtkTreePath of the last row activated in a column (the last row that
2442  * emitted a #GtkTreeView::row-activated signal). This is mainly useful if the
2443  * @selector #HildonTouchSelector:hildon-ui-mode in set to %HILDON_UI_MODE_NORMAL,
2444  * as using this state there is no real selection, so a method like
2445  * hildon_touch_selector_get_selected_rows() will return always a empty
2446  * selection.
2447  *
2448  * Anyway, this method works as well on %HILDON_UI_MODE_EDIT, but in this case
2449  * is better, and more useful, to get the current selection.
2450  *
2451  * Returns: a newly allocated #GtkTreePath pointing to the last activated row
2452  *          NULL if no row were activated.
2453  *
2454  * Since: 2.2
2455  **/
2456 GtkTreePath*
2457 hildon_touch_selector_get_last_activated_row    (HildonTouchSelector *selector,
2458                                                  gint                 column)
2459 {
2460   HildonTouchSelectorColumn *selector_column = NULL;
2461
2462   /* this method with check selector and that the column number is correct*/
2463   selector_column = hildon_touch_selector_get_column (selector, column);
2464
2465   if (selector_column == NULL) {
2466     return NULL;
2467   }
2468
2469   if (selector_column->priv->last_activated != NULL) {
2470     return gtk_tree_row_reference_get_path (selector_column->priv->last_activated);
2471   } else {
2472     return NULL;
2473   }
2474 }