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