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