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