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