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