2008-11-19 Alejandro Pinheiro <apinheiro@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  */
69
70 /**
71  * SECTION:hildon-touch-selector-column
72  * @short_description: A visible column in a #HildonTouchSelector
73  *
74  * #HildonTouchSelectorColumn object represents a visible column in
75  * #HildonTouchSelector. It allows to manage the cell renderers related to each
76  * column.
77  */
78
79 #undef HILDON_DISABLE_DEPRECATED
80
81 #ifdef HAVE_CONFIG_H
82 #include <config.h>
83 #endif
84
85 #include <string.h>
86 #include <stdlib.h>
87
88 #include "hildon-gtk.h"
89
90 #include "hildon-pannable-area.h"
91 #include "hildon-touch-selector.h"
92
93 #define HILDON_TOUCH_SELECTOR_GET_PRIVATE(obj)                          \
94   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_TOUCH_SELECTOR, HildonTouchSelectorPrivate))
95
96 G_DEFINE_TYPE (HildonTouchSelector, hildon_touch_selector, GTK_TYPE_VBOX)
97
98 /*
99  * IMPLEMENTATION NOTES:
100  * Struct to maintain the data of each column. The columns are the elements
101  * of the widget that belongs properly to the selection behaviour. Although
102  * internally the columns are arranged in a private #GtkHBox, as the selector
103  * itself is a #GtkVBox, you can add more widgets, like buttons etc., so
104  * you finally could have a widget with more elements that the columns, but
105  * this doesn't belongs to the selection logic
106  */
107 struct _HildonTouchSelectorColumnPrivate
108 {
109   HildonTouchSelector *parent;    /* the selector that contains this column */
110   GtkTreeModel *model;
111   gint text_column;
112   GtkTreeView *tree_view;
113
114   GtkWidget *panarea;           /* the pannable widget */
115 };
116
117 struct _HildonTouchSelectorPrivate
118 {
119   GSList *columns;              /* the selection columns */
120   GtkWidget *hbox;              /* the container for the selector's columns */
121   gboolean initial_scroll;      /* whether initial fancy scrolling to selection */
122
123   HildonTouchSelectorPrintFunc print_func;
124 };
125
126 enum
127 {
128   PROP_HAS_MULTIPLE_SELECTION = 1,
129   PROP_INITIAL_SCROLL
130 };
131
132 enum
133 {
134   CHANGED,
135   LAST_SIGNAL
136 };
137
138 static gint hildon_touch_selector_signals[LAST_SIGNAL] = { 0 };
139
140 static void
141 hildon_touch_selector_get_property              (GObject * object,
142                                                  guint prop_id,
143                                                  GValue * value,
144                                                  GParamSpec * pspec);
145 static void
146 hildon_touch_selector_set_property              (GObject *object,
147                                                  guint prop_id,
148                                                  const GValue *value,
149                                                  GParamSpec *pspec);
150 /* gtkwidget */
151
152 /* gtkcontainer */
153 static void hildon_touch_selector_remove        (GtkContainer * container,
154                                                  GtkWidget * widget);
155 /* private functions */
156 static void _selection_changed_cb               (GtkTreeSelection * selection,
157                                                  gpointer user_data);
158 static gchar *_default_print_func               (HildonTouchSelector * selector);
159
160 static HildonTouchSelectorColumn *_create_new_column (HildonTouchSelector * selector,
161                                                  GtkTreeModel * model,
162                                                  GtkCellRenderer * renderer,
163                                                  va_list args);
164 static gboolean
165 _hildon_touch_selector_center_on_selected_items (GtkWidget *widget,
166                                                  gpointer data);
167
168 static void
169 _hildon_touch_selector_set_model                (HildonTouchSelector * selector,
170                                                  gint num_column,
171                                                  GtkTreeModel * model);
172 static gboolean
173 _hildon_touch_selector_has_multiple_selection   (HildonTouchSelector * selector);
174
175 /* GtkCellLayout implementation (HildonTouchSelectorColumn)*/
176 static void hildon_touch_selector_column_cell_layout_init         (GtkCellLayoutIface      *iface);
177
178 static void hildon_touch_selector_column_cell_layout_pack_start   (GtkCellLayout         *cell_layout,
179                                                                    GtkCellRenderer       *cell,
180                                                                    gboolean               expand);
181 static void hildon_touch_selector_column_cell_layout_pack_end     (GtkCellLayout         *cell_layout,
182                                                                    GtkCellRenderer       *cell,
183                                                                    gboolean               expand);
184 static void hildon_touch_selector_column_cell_layout_clear        (GtkCellLayout         *cell_layout);
185 static void hildon_touch_selector_column_cell_layout_add_attribute(GtkCellLayout         *cell_layout,
186                                                                    GtkCellRenderer       *cell,
187                                                                    const gchar           *attribute,
188                                                                    gint                   column);
189 static void hildon_touch_selector_column_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
190                                                                          GtkCellRenderer       *cell,
191                                                                          GtkCellLayoutDataFunc  func,
192                                                                          gpointer               func_data,
193                                                                          GDestroyNotify         destroy);
194 static void hildon_touch_selector_column_cell_layout_clear_attributes   (GtkCellLayout         *cell_layout,
195                                                                          GtkCellRenderer       *cell);
196 static void hildon_touch_selector_column_cell_layout_reorder       (GtkCellLayout         *cell_layout,
197                                                                     GtkCellRenderer       *cell,
198                                                                     gint                   position);
199 static GList *hildon_touch_selector_column_cell_layout_get_cells   (GtkCellLayout         *cell_layout);
200
201
202 static void
203 hildon_touch_selector_class_init (HildonTouchSelectorClass * class)
204 {
205   GObjectClass *gobject_class;
206   GtkObjectClass *object_class;
207   GtkWidgetClass *widget_class;
208   GtkContainerClass *container_class;
209
210   gobject_class = G_OBJECT_CLASS (class);
211   object_class = GTK_OBJECT_CLASS (class);
212   widget_class = GTK_WIDGET_CLASS (class);
213   container_class = GTK_CONTAINER_CLASS (class);
214
215   /* GObject */
216   gobject_class->get_property = hildon_touch_selector_get_property;
217   gobject_class->set_property = hildon_touch_selector_set_property;
218
219   /* GtkWidget */
220
221   /* GtkContainer */
222   container_class->remove = hildon_touch_selector_remove;
223
224   /* HildonTouchSelector */
225   class->changed = NULL;
226   class->set_model = _hildon_touch_selector_set_model;
227
228   class->has_multiple_selection = _hildon_touch_selector_has_multiple_selection;
229
230   /* signals */
231   /**
232    * HildonTouchSelector::changed:
233    * @column: the concrete column being modified
234    * @widget: the object which received the signal
235    *
236    * The changed signal is emitted when the active item on any column is changed.
237    * This can be due to the user selecting a different item from the list, or
238    * due to a call to hildon_touch_selector_select_iter() on one of the columns.
239    *
240    */
241   hildon_touch_selector_signals[CHANGED] =
242     g_signal_new ("changed",
243                   G_OBJECT_CLASS_TYPE (class),
244                   G_SIGNAL_RUN_LAST,
245                   G_STRUCT_OFFSET (HildonTouchSelectorClass, changed),
246                   NULL, NULL,
247                   gtk_marshal_NONE__INT, G_TYPE_NONE, 1, G_TYPE_INT);
248   /* properties */
249
250   g_object_class_install_property (gobject_class, PROP_HAS_MULTIPLE_SELECTION,
251                                    g_param_spec_boolean ("has-multiple-selection",
252                                                          "has multiple selection",
253                                                          "Whether the widget has multiple "
254                                                          "selection (like multiple columns, "
255                                                          "multiselection mode, or multiple "
256                                                          "internal widgets) and therefore "
257                                                          "it may need a confirmation button, "
258                                                          "for instance.",
259                                                          FALSE,
260                                                          G_PARAM_READABLE));
261
262   g_object_class_install_property (G_OBJECT_CLASS (gobject_class),
263                                    PROP_INITIAL_SCROLL,
264                                    g_param_spec_boolean ("initial-scroll",
265                                                          "Initial scroll",
266                                                          "Whether to scroll to the"
267                                                          "current selection when"
268                                                          "the selector is first"
269                                                          "shown",
270                                                          TRUE,
271                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
272
273   /* style properties */
274   /* We need to ensure fremantle mode for the treeview in order to work
275      properly. This is not about the appearance, this is about behaviour */
276   gtk_rc_parse_string ("style \"fremantle-htst\" {\n"
277                        "  GtkWidget::hildon-mode = 1\n"
278                        "} widget \"*.fremantle-htst\" style \"fremantle-htst\""
279                        "widget_class \"*<HildonPannableArea>.GtkTreeView\" style :highest \"fremantle-htst\"");
280
281   g_type_class_add_private (object_class, sizeof (HildonTouchSelectorPrivate));
282 }
283
284 static void
285 hildon_touch_selector_get_property (GObject * object,
286                                     guint prop_id,
287                                     GValue * value, GParamSpec * pspec)
288 {
289   HildonTouchSelectorPrivate *priv = HILDON_TOUCH_SELECTOR (object)->priv;
290
291   switch (prop_id) {
292   case PROP_HAS_MULTIPLE_SELECTION:
293     g_value_set_boolean (value,
294                          hildon_touch_selector_has_multiple_selection (HILDON_TOUCH_SELECTOR (object)));
295     break;
296   case PROP_INITIAL_SCROLL:
297     g_value_set_boolean (value, priv->initial_scroll);
298     break;
299   default:
300     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
301     break;
302   }
303 }
304
305 static void
306 hildon_touch_selector_set_property (GObject *object, guint prop_id,
307                                     const GValue *value, GParamSpec *pspec)
308 {
309   HildonTouchSelectorPrivate *priv = HILDON_TOUCH_SELECTOR (object)->priv;
310
311   switch (prop_id) {
312   case PROP_INITIAL_SCROLL:
313     priv->initial_scroll = g_value_get_boolean (value);
314     break;
315   default:
316     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
317     break;
318   }
319 }
320
321
322 static void
323 hildon_touch_selector_init (HildonTouchSelector * selector)
324 {
325   selector->priv = HILDON_TOUCH_SELECTOR_GET_PRIVATE (selector);
326
327   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (selector), GTK_NO_WINDOW);
328   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (selector), FALSE);
329
330   selector->priv->columns = NULL;
331
332   selector->priv->print_func = NULL;
333   selector->priv->initial_scroll = TRUE;
334   selector->priv->hbox = gtk_hbox_new (FALSE, 0);
335
336   gtk_box_pack_end (GTK_BOX (selector), selector->priv->hbox,
337                     TRUE, TRUE, 0);
338   gtk_widget_show (selector->priv->hbox);
339 }
340
341 /*
342  * IMPLEMENTATION NOTES:
343  * Some people sent questions regarding a missing dispose/finalize function on
344  * this widget that could lead to leak memory, so we will clarify this topic.
345  *
346  * This is not required as #HildonTouchSelector extends #GtkContainer. When the
347  * widget is freed, the #GtkContainer freeing memory functions are called. This
348  * process includes remove each widget individually, so all the widgets are
349  * properly freed.
350  *
351  * In the same way, this widget redefines gtk_container->remove function, in
352  * order to free the column related information if it is required.
353  *
354  * Please take a look to hildon_touch_selector_remove for more information.
355  */
356
357 /*------------------------------ GtkContainer ------------------------------ */
358
359 /*
360  * Required in order to free the column at the columns list
361  */
362 static void
363 hildon_touch_selector_remove (GtkContainer * container, GtkWidget * widget)
364 {
365   HildonTouchSelector *selector = NULL;
366
367   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (container));
368   selector = HILDON_TOUCH_SELECTOR (container);
369
370   /* Remove the extra data related to the columns, if required. */
371   if (widget == selector->priv->hbox) {
372     g_slist_foreach (selector->priv->columns, (GFunc) g_object_unref, NULL);
373
374     g_slist_free (selector->priv->columns);
375
376     selector->priv->columns = NULL;
377   } else {
378     g_debug ("Freeing a widget outside the columns logic");
379   }
380
381   /* Now remove the widget itself from the container */
382   GTK_CONTAINER_CLASS (hildon_touch_selector_parent_class)->remove (container, widget);
383 }
384
385 /* ------------------------------ PRIVATE METHODS ---------------------------- */
386 /**
387  * default_print_func:
388  * @selector: a #HildonTouchSelector
389  *
390  * Default print function
391  *
392  * Returns: a new string that represents the selected items
393  **/
394 static gchar *
395 _default_print_func (HildonTouchSelector * selector)
396 {
397   gchar *result = NULL;
398   gchar *aux = NULL;
399   gint num_columns = 0;
400   GtkTreeIter iter;
401   GtkTreeModel *model = NULL;
402   gchar *current_string = NULL;
403   gint i;
404   HildonTouchSelectorSelectionMode mode;
405   GList *item = NULL;
406   GtkTreePath *current_path = NULL;
407   GList *selected_rows = NULL;
408   gint initial_value = 0;
409   gint text_column = -1; 
410   HildonTouchSelectorColumn *column = NULL;
411
412   num_columns = hildon_touch_selector_get_num_columns (selector);
413
414   mode = hildon_touch_selector_get_column_selection_mode (selector);
415
416   if ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
417       && (num_columns > 0)) {
418     /* In this case we get the first column first */
419     selected_rows = hildon_touch_selector_get_selected_rows (selector, 0);
420     model = hildon_touch_selector_get_model (selector, 0);
421     column = hildon_touch_selector_get_column (selector, 0);
422     g_object_get (G_OBJECT(column), "text-column", &text_column, NULL);
423
424     result = g_strdup_printf ("(");
425     i = 0;
426     for (item = selected_rows; item; item = g_list_next (item)) {
427       current_path = item->data;
428       gtk_tree_model_get_iter (model, &iter, current_path);
429
430       if (text_column != -1) {
431         gtk_tree_model_get (model, &iter, text_column, &current_string, -1);
432       }
433
434       if (i < g_list_length (selected_rows) - 1) {
435         aux = g_strconcat (result, current_string, ",", NULL);
436         g_free (result);
437         result = aux;
438       } else {
439         aux = g_strconcat (result, current_string, NULL);
440         g_free (result);
441         result = aux;
442       }
443       i++;
444     }
445
446     aux = g_strconcat (result, ")", NULL);
447     g_free (result);
448     result = aux;
449
450     g_list_foreach (selected_rows, (GFunc) (gtk_tree_path_free), NULL);
451     g_list_free (selected_rows);
452     initial_value = 1;
453   } else {
454     initial_value = 0;
455   }
456
457   for (i = initial_value; i < num_columns; i++) {
458     model = hildon_touch_selector_get_model (selector, i);
459     column = hildon_touch_selector_get_column (selector, 0);
460     g_object_get (G_OBJECT(column), "text-column", &text_column, NULL);
461
462     if (hildon_touch_selector_get_selected (selector, i, &iter)) {
463
464       if (text_column != -1 ) {
465         gtk_tree_model_get (model, &iter, text_column, &current_string, -1);
466       }
467       if (i != 0) {
468         aux = g_strconcat (result, ":", current_string, NULL);
469         g_free (result);
470         result = aux;
471       } else {
472         result = current_string;
473       }
474     }
475   }
476
477   return result;
478 }
479
480 static void
481 _selection_changed_cb (GtkTreeSelection * selection, gpointer user_data)
482 {
483   HildonTouchSelector *selector = NULL;
484   HildonTouchSelectorColumn *column = NULL;
485   gint num_column = -1;
486
487   column = HILDON_TOUCH_SELECTOR_COLUMN (user_data);
488   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (column->priv->parent));
489
490   selector = column->priv->parent;
491
492   num_column = g_slist_index (selector->priv->columns, column);
493
494   g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, num_column);
495 }
496
497
498 static HildonTouchSelectorColumn *
499 _create_new_column (HildonTouchSelector * selector,
500                     GtkTreeModel * model,
501                     GtkCellRenderer * renderer, va_list args)
502 {
503   HildonTouchSelectorColumn *new_column = NULL;
504   GtkTreeViewColumn *tree_column = NULL;
505   GtkTreeView *tv = NULL;
506   GtkWidget *panarea = NULL;
507   GtkTreeSelection *selection = NULL;
508   GtkTreeIter iter;
509   gchar *attribute;
510   gint value;
511
512   tree_column = gtk_tree_view_column_new ();
513
514   if (renderer != NULL) {
515     gtk_tree_view_column_pack_start (tree_column, renderer, TRUE);
516
517     attribute = va_arg (args, gchar *);
518     while (attribute != NULL) {
519       value = va_arg (args, gint);
520       gtk_tree_view_column_add_attribute (tree_column, renderer, attribute,
521                                           value);
522       attribute = va_arg (args, gchar *);
523     }
524   }
525
526 #ifdef MAEMO_GTK
527   tv = GTK_TREE_VIEW (hildon_gtk_tree_view_new (HILDON_UI_MODE_EDIT));
528 #else
529   tv = GTK_TREE_VIEW (gtk_tree_view_new ());
530 #endif /* MAEMO_GTK */
531
532   gtk_tree_view_set_enable_search (tv, FALSE);
533
534   gtk_tree_view_set_model (tv, model);
535   gtk_tree_view_set_rules_hint (tv, TRUE);
536
537   gtk_tree_view_append_column (GTK_TREE_VIEW (tv), tree_column);
538
539   new_column = g_object_new (HILDON_TYPE_TOUCH_SELECTOR_COLUMN, NULL);
540   new_column->priv->parent = selector;
541
542   panarea = hildon_pannable_area_new ();
543
544   g_object_set (G_OBJECT (panarea), "vscrollbar-policy", GTK_POLICY_NEVER,
545                 "initial-hint", FALSE, NULL);
546
547   gtk_container_add (GTK_CONTAINER (panarea), GTK_WIDGET (tv));
548
549   new_column->priv->model = model;
550   new_column->priv->tree_view = tv;
551   new_column->priv->panarea = panarea;
552
553   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
554   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
555
556   /* select the first item */
557   if (gtk_tree_model_get_iter_first (model, &iter)) {
558     gtk_tree_selection_select_iter (selection, &iter);
559   }
560
561   gtk_widget_grab_focus (GTK_WIDGET (tv));
562
563   /* connect to the changed signal connection */
564   g_signal_connect (G_OBJECT (selection), "changed",
565                     G_CALLBACK (_selection_changed_cb), new_column);
566
567   g_signal_connect_after (G_OBJECT (panarea), "realize",
568                           G_CALLBACK (_hildon_touch_selector_center_on_selected_items),
569                           new_column);
570
571   return new_column;
572 }
573
574
575 /* ------------------------ HildonTouchSelectorColumn implementation ---------------------- */
576 G_DEFINE_TYPE_WITH_CODE (HildonTouchSelectorColumn, hildon_touch_selector_column, G_TYPE_OBJECT,
577                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
578                                                 hildon_touch_selector_column_cell_layout_init))
579
580 enum
581 {
582   PROP_TEXT_COLUMN = 1
583 };
584
585 static void
586 hildon_touch_selector_column_class_init (HildonTouchSelectorColumnClass *klass);
587
588 static void
589 hildon_touch_selector_column_get_property (GObject *object, guint property_id,
590                                            GValue *value, GParamSpec *pspec);
591
592 static void
593 hildon_touch_selector_column_set_property  (GObject *object, guint property_id,
594                                             const GValue *value, GParamSpec *pspec);
595
596 static void
597 hildon_touch_selector_column_set_text_column (HildonTouchSelectorColumn *column,
598                                               gint text_column);
599 static gint
600 hildon_touch_selector_column_get_text_column (HildonTouchSelectorColumn *column);
601
602
603 static void
604 hildon_touch_selector_column_class_init (HildonTouchSelectorColumnClass *klass)
605 {
606   GObjectClass *gobject_class = NULL;
607
608   gobject_class = G_OBJECT_CLASS (klass);
609
610   g_type_class_add_private (gobject_class, sizeof (HildonTouchSelectorColumnPrivate));
611
612   /* GObject */
613   gobject_class->get_property = hildon_touch_selector_column_get_property;
614   gobject_class->set_property = hildon_touch_selector_column_set_property;
615
616   g_object_class_install_property (G_OBJECT_CLASS(klass),
617                                    PROP_TEXT_COLUMN,
618                                    g_param_spec_int ("text-column",
619                                                      "Text Column",
620                                                      "A column in the data source model to get the strings from.",
621                                                      -1,
622                                                      G_MAXINT,
623                                                      -1,
624                                                      G_PARAM_READWRITE));
625 }
626
627 static void
628 hildon_touch_selector_column_init (HildonTouchSelectorColumn *column)
629 {
630   column->priv = G_TYPE_INSTANCE_GET_PRIVATE (column, HILDON_TYPE_TOUCH_SELECTOR_COLUMN,
631                                               HildonTouchSelectorColumnPrivate);
632   column->priv->text_column = -1;
633 }
634
635 static void
636 hildon_touch_selector_column_set_text_column (HildonTouchSelectorColumn *column,
637                                               gint text_column)
638 {
639   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (column));
640   g_return_if_fail (text_column >= -1);
641
642   column->priv->text_column = text_column;
643
644   g_object_notify (G_OBJECT (column), "text-column");
645 }
646
647 static gint
648 hildon_touch_selector_column_get_text_column (HildonTouchSelectorColumn *column)
649 {
650   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (column), -1);
651
652   return column->priv->text_column;
653 }
654
655 static void
656 hildon_touch_selector_column_get_property (GObject *object, guint property_id,
657                                            GValue *value, GParamSpec *pspec)
658 {
659   switch (property_id) {
660   case PROP_TEXT_COLUMN:
661     g_value_set_int (value,
662                      hildon_touch_selector_column_get_text_column (HILDON_TOUCH_SELECTOR_COLUMN (object)));
663     break;
664   default:
665     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
666   }
667 }
668
669 static void
670 hildon_touch_selector_column_set_property (GObject *object, guint property_id,
671                                            const GValue *value, GParamSpec *pspec)
672 {
673   switch (property_id) {
674   case PROP_TEXT_COLUMN:
675     hildon_touch_selector_column_set_text_column (HILDON_TOUCH_SELECTOR_COLUMN (object),
676                                                   g_value_get_int (value));
677     break;
678   default:
679     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
680   }
681 }
682
683 /* ------------------------ GtkCellLayout implementation -------------------- */
684 static void
685 hildon_touch_selector_column_cell_layout_init (GtkCellLayoutIface      *iface)
686 {
687   iface->pack_start         = hildon_touch_selector_column_cell_layout_pack_start;
688   iface->pack_end           = hildon_touch_selector_column_cell_layout_pack_end;
689   iface->clear              = hildon_touch_selector_column_cell_layout_clear;
690   iface->add_attribute      = hildon_touch_selector_column_cell_layout_add_attribute;
691   iface->set_cell_data_func = hildon_touch_selector_column_cell_layout_set_cell_data_func;
692   iface->clear_attributes   = hildon_touch_selector_column_cell_layout_clear_attributes;
693   iface->reorder            = hildon_touch_selector_column_cell_layout_reorder;
694   iface->get_cells          = hildon_touch_selector_column_cell_layout_get_cells;
695 }
696
697 static void
698 hildon_touch_selector_column_cell_layout_pack_start (GtkCellLayout         *cell_layout,
699                                                GtkCellRenderer       *cell,
700                                                gboolean               expand)
701 {
702   HildonTouchSelectorColumn *sel_column = NULL;
703   GtkTreeViewColumn *view_column = NULL;
704
705   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
706   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
707
708   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
709
710   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(view_column), cell, expand);
711
712 }
713
714 static void
715 hildon_touch_selector_column_cell_layout_pack_end (GtkCellLayout         *cell_layout,
716                                              GtkCellRenderer       *cell,
717                                              gboolean               expand)
718 {
719   HildonTouchSelectorColumn *sel_column = NULL;
720   GtkTreeViewColumn *view_column = NULL;
721
722   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
723   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
724
725   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
726
727   gtk_cell_layout_pack_end (GTK_CELL_LAYOUT(view_column), cell, expand);
728 }
729
730 static void
731 hildon_touch_selector_column_cell_layout_clear (GtkCellLayout         *cell_layout)
732 {
733   HildonTouchSelectorColumn *sel_column = NULL;
734   GtkTreeViewColumn *view_column = NULL;
735
736   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
737   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
738
739   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
740
741   gtk_cell_layout_clear (GTK_CELL_LAYOUT(view_column));
742 }
743
744 static void
745 hildon_touch_selector_column_cell_layout_add_attribute (GtkCellLayout         *cell_layout,
746                                                   GtkCellRenderer       *cell,
747                                                   const gchar           *attribute,
748                                                   gint                   column)
749 {
750   HildonTouchSelectorColumn *sel_column = NULL;
751   GtkTreeViewColumn *view_column = NULL;
752
753   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
754   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
755
756   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
757
758   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(view_column), cell, attribute, column);
759 }
760
761
762 static void
763 hildon_touch_selector_column_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
764                                                        GtkCellRenderer       *cell,
765                                                        GtkCellLayoutDataFunc  func,
766                                                        gpointer               func_data,
767                                                        GDestroyNotify         destroy)
768 {
769   HildonTouchSelectorColumn *sel_column = NULL;
770   GtkTreeViewColumn *view_column = NULL;
771
772   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
773   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
774
775   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
776
777   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(view_column), cell, func,
778                                       func_data, destroy);
779 }
780
781 static void
782 hildon_touch_selector_column_cell_layout_clear_attributes (GtkCellLayout         *cell_layout,
783                                                      GtkCellRenderer       *cell)
784 {
785   HildonTouchSelectorColumn *sel_column = NULL;
786   GtkTreeViewColumn *view_column = NULL;
787
788   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
789   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
790
791   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
792
793   gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (view_column), cell);
794 }
795
796 static void
797 hildon_touch_selector_column_cell_layout_reorder (GtkCellLayout         *cell_layout,
798                                             GtkCellRenderer       *cell,
799                                             gint                   position)
800 {
801   HildonTouchSelectorColumn *sel_column = NULL;
802   GtkTreeViewColumn *view_column = NULL;
803
804   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
805   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
806
807   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
808
809   gtk_cell_layout_reorder (GTK_CELL_LAYOUT(view_column), cell, position);
810 }
811
812 static GList*
813 hildon_touch_selector_column_cell_layout_get_cells (GtkCellLayout         *cell_layout)
814 {
815   HildonTouchSelectorColumn *sel_column = NULL;
816   GtkTreeViewColumn *view_column = NULL;
817
818   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout), NULL);
819   sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
820
821   view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
822
823   return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(view_column));
824 }
825
826 /* ------------------------------ PUBLIC METHODS ---------------------------- */
827
828 /**
829  * hildon_touch_selector_new:
830  *
831  * Creates a new empty #HildonTouchSelector.
832  *
833  * Returns: a new #HildonTouchSelector.
834  **/
835 GtkWidget *
836 hildon_touch_selector_new (void)
837 {
838   return g_object_new (HILDON_TYPE_TOUCH_SELECTOR, NULL);
839 }
840
841 /**
842  * hildon_touch_selector_new_text:
843  *
844  * Creates a #HildonTouchSelector with a single text column that
845  * can be populated conveniently through hildon_touch_selector_append_text(),
846  * hildon_touch_selector_prepend_text(), hildon_touch_selector_insert_text().
847  *
848  * Returns: A new #HildonTouchSelector
849  **/
850 GtkWidget *
851 hildon_touch_selector_new_text (void)
852 {
853   GtkWidget *selector;
854   GtkListStore *store;
855   HildonTouchSelectorColumn *column = NULL;
856
857   selector = hildon_touch_selector_new ();
858   store = gtk_list_store_new (1, G_TYPE_STRING);
859
860   column = hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
861                                                      GTK_TREE_MODEL (store), TRUE);
862
863   g_object_set (G_OBJECT (column), "text-column", 0, NULL);
864
865   return selector;
866 }
867
868 /**
869  * hildon_touch_selector_append_text:
870  * @selector: A #HildonTouchSelector.
871  * @text: a non %NULL text string.
872  *
873  * Appends a new entry in a #HildonTouchSelector created with
874  * hildon_touch_selector_new_text().
875  **/
876 void
877 hildon_touch_selector_append_text (HildonTouchSelector * selector,
878                                    const gchar * text)
879 {
880   GtkTreeIter iter;
881   GtkTreeModel *model;
882
883   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
884   g_return_if_fail (text != NULL);
885
886   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
887
888   g_return_if_fail (GTK_IS_LIST_STORE (model));
889
890   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
891   gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, text, -1);
892 }
893
894 /**
895  * hildon_touch_selector_prepend_text:
896  * @selector: A #HildonTouchSelector.
897  * @text: a non %NULL text string.
898  *
899  * Prepends a new entry in a #HildonTouchSelector created with
900  * hildon_touch_selector_new_text().
901  **/
902 void
903 hildon_touch_selector_prepend_text (HildonTouchSelector * selector,
904                                     const gchar * text)
905 {
906   GtkTreeIter iter;
907   GtkTreeModel *model;
908
909   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
910   g_return_if_fail (text != NULL);
911
912   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
913
914   g_return_if_fail (GTK_IS_LIST_STORE (model));
915
916   gtk_list_store_prepend (GTK_LIST_STORE (model), &iter);
917   gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, text, -1);
918 }
919
920 /**
921  * hildon_touch_selector_insert_text:
922  * @selector: a #HildonTouchSelector.
923  * @position: the position to insert @text.
924  * @text: A non %NULL text string.
925  *
926  * Inserts a new entry in particular position of a #HildoTouchSelector created
927  * with hildon_touch_selector_new_text().
928  *
929  **/
930 void
931 hildon_touch_selector_insert_text (HildonTouchSelector * selector,
932                                    gint position, const gchar * text)
933 {
934   GtkTreeIter iter;
935   GtkTreeModel *model;
936
937   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
938   g_return_if_fail (text != NULL);
939   g_return_if_fail (position >= 0);
940
941   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
942
943   g_return_if_fail (GTK_IS_LIST_STORE (model));
944
945   gtk_list_store_insert (GTK_LIST_STORE (model), &iter, position);
946   gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, text, -1);
947 }
948
949 /**
950  * hildon_touch_selector_append_column
951  * @selector: a #HildonTouchSelector
952  * @model: the #GtkTreeModel with the data of the column
953  * @cell_renderer: The #GtkCellRenderer where to draw each row contents.
954  * @Varargs: a %NULL-terminated pair of attributes and column numbers.
955  *
956  * This functions adds a new column to the widget, whose data will
957  * be obtained from the model. Only widgets added this way should used on
958  * the selection logic, i.e., the print function, the #HildonTouchPicker::changed
959  * signal, etc.
960  *
961  * You can optionally pass a #GtkCellRenderer in @cell_renderer,
962  * together with a %NULL-terminated list of pairs property/value, in
963  * the same way you would use gtk_tree_view_column_set_attributes().
964  * This will pack @cell_renderer at the start of the column, expanded by default.
965  * If you prefer not to add it this way, you can simply pass %NULL to @cell_renderer
966  * and use the #GtkCellLayout interface on the returned #HildonTouchSelectorColumn
967  * to set your renderers.
968  *
969  * There is a prerequisite to be considered on models used: text data must
970  * be in the first column.
971  *
972  * This method basically adds a #GtkTreeView to the widget, using the model and
973  * the data received.
974  *
975  * Returns: the new column added added, %NULL otherwise.
976  **/
977
978 HildonTouchSelectorColumn*
979 hildon_touch_selector_append_column (HildonTouchSelector * selector,
980                                      GtkTreeModel * model,
981                                      GtkCellRenderer * cell_renderer, ...)
982 {
983   va_list args;
984   HildonTouchSelectorColumn *new_column = NULL;
985
986   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
987   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
988
989   if (model != NULL) {
990
991     va_start (args, cell_renderer);
992     new_column = _create_new_column (selector, model, cell_renderer, args);
993     g_object_ref_sink (new_column);
994     va_end (args);
995
996     selector->priv->columns = g_slist_append (selector->priv->columns,
997                                               new_column);
998     gtk_box_pack_start (GTK_BOX (selector->priv->hbox),
999                         new_column->priv->panarea,
1000                         TRUE, TRUE, 6);
1001
1002     gtk_widget_show_all (new_column->priv->panarea);
1003   } else {
1004     return NULL;
1005   }
1006
1007   return new_column;
1008 }
1009
1010 /**
1011  * hildon_touch_selector_append_text_column
1012  * @selector: a #HildonTouchSelector
1013  * @model: a #GtkTreeModel with data for the column
1014  * @center: whether to center the text on the column
1015  *
1016  * Equivalent to hildon_touch_selector_append_column(), but using a
1017  * default text cell renderer. This is the most common use case of the
1018  * widget.
1019  *
1020  * Returns: the new column added, NULL otherwise.
1021  **/
1022 HildonTouchSelectorColumn*
1023 hildon_touch_selector_append_text_column (HildonTouchSelector * selector,
1024                                           GtkTreeModel * model, gboolean center)
1025 {
1026   GtkCellRenderer *renderer = NULL;
1027   GValue val = { 0, };
1028
1029   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1030   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
1031
1032   if (model != NULL) {
1033     renderer = gtk_cell_renderer_text_new ();
1034
1035     if (center) {
1036       g_value_init (&val, G_TYPE_FLOAT);
1037       g_value_set_float (&val, 0.5);
1038       g_object_set_property (G_OBJECT (renderer), "xalign", &val);
1039     }
1040
1041     return hildon_touch_selector_append_column (selector, model, renderer,
1042                                                 "text", 0, NULL);
1043   } else {
1044     return NULL;
1045   }
1046 }
1047
1048 /**
1049  * hildon_touch_selector_remove_column:
1050  * @selector: a #HildonTouchSelector
1051  * @column: the position of the column to be removed
1052  *
1053  * Removes a column from @selector.
1054  *
1055  * Returns: %TRUE if the column was removed, %FALSE otherwise
1056  **/
1057 gboolean
1058 hildon_touch_selector_remove_column (HildonTouchSelector * selector, gint column)
1059 {
1060   HildonTouchSelectorColumn *current_column = NULL;
1061   HildonTouchSelectorPrivate *priv;
1062
1063   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
1064   g_return_val_if_fail (column <
1065                         hildon_touch_selector_get_num_columns (selector), FALSE);
1066
1067   priv = HILDON_TOUCH_SELECTOR_GET_PRIVATE (selector);
1068   current_column = g_slist_nth_data (priv->columns, column);
1069
1070   gtk_container_remove (GTK_CONTAINER (priv->hbox), current_column->priv->panarea);
1071   priv->columns = g_slist_remove (priv->columns, current_column);
1072   g_object_unref (current_column);
1073
1074   return TRUE;
1075 }
1076
1077 /**
1078  * hildon_touch_selector_set_column_attributes:
1079  * @selector: a #HildonTouchSelector
1080  * @num_column: the number of the column whose attributes we're setting
1081  * @cell_renderer: the #GtkCellRendere we're setting the attributes of
1082  * @Varargs: A %NULL-terminated list of attributes.
1083  *
1084  * Sets the attributes for the given column. The attributes must be given
1085  * in attribute/column pairs, just like in gtk_tree_view_column_set_attributes().
1086  * All existing attributes are removed and replaced with the new ones.
1087  *
1088  * Deprecated: #HildonTouchSelectorColumn implements #GtkCellLayout, use this
1089  *             interface instead. See
1090  *             hildon_touch_selector_get_column().
1091  *
1092  **/
1093 void
1094 hildon_touch_selector_set_column_attributes (HildonTouchSelector * selector,
1095                                              gint num_column,
1096                                              GtkCellRenderer * cell_renderer,
1097                                              ...)
1098 {
1099   va_list args;
1100   GtkTreeViewColumn *tree_column = NULL;
1101   HildonTouchSelectorColumn *current_column = NULL;
1102   gchar *attribute = NULL;
1103   gint value = 0;
1104
1105   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1106   g_return_if_fail (num_column <
1107                     hildon_touch_selector_get_num_columns (selector));
1108
1109   current_column = g_slist_nth_data (selector->priv->columns, num_column);
1110
1111   tree_column = gtk_tree_view_get_column (current_column->priv->tree_view, 0);
1112   gtk_tree_view_remove_column (current_column->priv->tree_view, tree_column);
1113
1114   tree_column = gtk_tree_view_column_new ();
1115   gtk_tree_view_column_pack_start (tree_column, cell_renderer, TRUE);
1116
1117   va_start (args, cell_renderer);
1118   attribute = va_arg (args, gchar *);
1119
1120   gtk_tree_view_column_clear_attributes (tree_column, cell_renderer);
1121
1122   while (attribute != NULL) {
1123     value = va_arg (args, gint);
1124     gtk_tree_view_column_add_attribute (tree_column, cell_renderer,
1125                                         attribute, value);
1126     attribute = va_arg (args, gchar *);
1127   }
1128
1129   va_end (args);
1130
1131   gtk_tree_view_append_column (current_column->priv->tree_view, tree_column);
1132 }
1133
1134 /**
1135  * hildon_touch_selector_get_num_columns:
1136  * @selector: a #HildonTouchSelector
1137  *
1138  * Gets the number of columns in the #HildonTouchSelector.
1139  *
1140  * Returns: the number of columns in @selector.
1141  **/
1142 gint
1143 hildon_touch_selector_get_num_columns (HildonTouchSelector * selector)
1144 {
1145   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), -1);
1146
1147   return g_slist_length (selector->priv->columns);
1148 }
1149
1150 /**
1151  * hildon_touch_selector_get_column_selection_mode:
1152  * @selector: a #HildonTouchSelector
1153  *
1154  * Gets the selection mode of @selector.
1155  *
1156  * Returns: one of #HildonTouchSelectorSelectionMode
1157  **/
1158 HildonTouchSelectorSelectionMode
1159 hildon_touch_selector_get_column_selection_mode (HildonTouchSelector * selector)
1160 {
1161   HildonTouchSelectorSelectionMode result =
1162     HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
1163   GtkSelectionMode treeview_mode = GTK_SELECTION_BROWSE;
1164   HildonTouchSelectorColumn *column = NULL;
1165   GtkTreeSelection *selection = NULL;
1166
1167   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), result);
1168   g_return_val_if_fail (hildon_touch_selector_get_num_columns (selector) > 0,
1169                         result);
1170
1171   column = HILDON_TOUCH_SELECTOR_COLUMN (selector->priv->columns->data);
1172
1173   selection = gtk_tree_view_get_selection (column->priv->tree_view);
1174   treeview_mode = gtk_tree_selection_get_mode (selection);
1175
1176
1177   if (treeview_mode == GTK_SELECTION_MULTIPLE) {
1178     result = HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE;
1179   } else {
1180     result = HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
1181   }
1182
1183   return result;
1184 }
1185
1186 /**
1187  * hildon_touch_selector_set_column_selection_mode:
1188  * @selector: a #HildonTouchSelector
1189  * @mode: the #HildonTouchSelectorMode for @selector
1190  *
1191  * Sets the selection mode for @selector. See #HildonTouchSelectorSelectionMode.
1192  **/
1193 void
1194 hildon_touch_selector_set_column_selection_mode (HildonTouchSelector * selector,
1195                                                  HildonTouchSelectorSelectionMode mode)
1196 {
1197   GtkTreeView *tv = NULL;
1198   HildonTouchSelectorColumn *column = NULL;
1199   GtkTreeSelection *selection = NULL;
1200   GtkSelectionMode treeview_mode = GTK_SELECTION_MULTIPLE;
1201   GtkTreeIter iter;
1202   HildonTouchSelectorSelectionMode current_mode;
1203
1204   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1205   g_return_if_fail (hildon_touch_selector_get_num_columns (selector) > 0);
1206
1207   current_mode = hildon_touch_selector_get_column_selection_mode (selector);
1208
1209   if (current_mode == mode) {
1210     return;
1211   }
1212
1213   column = HILDON_TOUCH_SELECTOR_COLUMN ((g_slist_nth (selector->priv->columns, 0))->data);
1214   tv = column->priv->tree_view;
1215
1216   if (tv) {
1217     switch (mode) {
1218     case HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE:
1219       treeview_mode = GTK_SELECTION_BROWSE;
1220       break;
1221     case HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE:
1222       treeview_mode = GTK_SELECTION_MULTIPLE;
1223       break;
1224     }
1225
1226     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
1227     gtk_tree_selection_set_mode (selection, treeview_mode);
1228
1229     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
1230     gtk_tree_model_get_iter_first (column->priv->model, &iter);
1231     gtk_tree_selection_unselect_all (selection);
1232     gtk_tree_selection_select_iter (selection, &iter);
1233   }
1234
1235 }
1236
1237 /**
1238  * hildon_touch_selector_set_print_func:
1239  * @selector: a #HildonTouchSelector
1240  * @func: a #HildonTouchSelectorPrintFunc function
1241  *
1242  * Sets the function to be used by hildon_touch_selector_get_current_text()
1243  * to produce a text representation of the currently selected items in @selector.
1244  * The default function will return a concatenation of comma separated items
1245  * selected in each column in @selector. Use this to override this method if you
1246  * need a particular representation for your application.
1247  *
1248  **/
1249 void
1250 hildon_touch_selector_set_print_func (HildonTouchSelector * selector,
1251                                       HildonTouchSelectorPrintFunc func)
1252 {
1253   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1254
1255   selector->priv->print_func = func;
1256 }
1257
1258 /**
1259  * hildon_touch_selector_get_print_func:
1260  * @selector: a #HildonTouchSelector
1261  *
1262  * Gets the #HildonTouchSelectorPrintFunc currently used. See
1263  * hildon_touch_selector_set_print_func().
1264  *
1265  * Returns: a #HildonTouchSelectorPrintFunc or %NULL if the default
1266  * one is currently used.
1267  **/
1268 HildonTouchSelectorPrintFunc
1269 hildon_touch_selector_get_print_func (HildonTouchSelector * selector)
1270 {
1271   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1272
1273   return selector->priv->print_func;
1274 }
1275
1276 /**
1277  * hildon_touch_selector_set_active:
1278  * @selector: a #HildonTouchSelector
1279  * @column: column number
1280  * @index: the index of the item to select, or -1 to have no active item
1281  *
1282  * Sets the active item of the #HildonTouchSelector to @index. The
1283  * column number is taken from @column.
1284  *
1285  * @selector must be in %HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE
1286  **/
1287 void
1288 hildon_touch_selector_set_active                (HildonTouchSelector *selector,
1289                                                  gint                 column,
1290                                                  gint                 index)
1291 {
1292   GtkTreeSelection *selection = NULL;
1293   HildonTouchSelectorColumn *current_column = NULL;
1294   HildonTouchSelectorSelectionMode mode;
1295   GtkTreePath *path;
1296
1297   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1298   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
1299   mode = hildon_touch_selector_get_column_selection_mode (selector);
1300   g_return_if_fail (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE);
1301
1302   current_column = g_slist_nth_data (selector->priv->columns, column);
1303
1304   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
1305   path = gtk_tree_path_new_from_indices (index, -1);
1306   gtk_tree_selection_unselect_all (selection);
1307   if (index != -1)
1308     gtk_tree_selection_select_path (selection, path);
1309
1310   gtk_tree_path_free (path);
1311 }
1312
1313 /**
1314  * hildon_touch_selector_get_active:
1315  * @selector: a #HildonTouchSelector
1316  * @column: column number
1317  *
1318  * Returns the index of the currently active item in column number
1319  * @column, or -1 if there's no active item.
1320  *
1321  * @selector must be in %HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE
1322  *
1323  * Returns: an integer which is the index of the currently active
1324  * item, or -1 if there's no active item.
1325  **/
1326 gint
1327 hildon_touch_selector_get_active                (HildonTouchSelector *selector,
1328                                                  gint                 column)
1329 {
1330   GtkTreeSelection *selection = NULL;
1331   HildonTouchSelectorColumn *current_column = NULL;
1332   HildonTouchSelectorSelectionMode mode;
1333   GtkTreeModel *model;
1334   GtkTreeIter iter;
1335   GtkTreePath *path;
1336   gint index;
1337
1338   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), -1);
1339   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector), -1);
1340   mode = hildon_touch_selector_get_column_selection_mode (selector);
1341   g_return_val_if_fail (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE, -1);
1342
1343   current_column = g_slist_nth_data (selector->priv->columns, column);
1344
1345   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
1346   model = gtk_tree_view_get_model (GTK_TREE_VIEW (current_column->priv->tree_view));
1347
1348   gtk_tree_selection_get_selected (selection, NULL, &iter);
1349   path = gtk_tree_model_get_path (model, &iter);
1350   index = (gtk_tree_path_get_indices (path))[0];
1351
1352   gtk_tree_path_free (path);
1353
1354   return index;
1355 }
1356
1357 /**
1358  * hildon_touch_selector_get_selected:
1359  * @selector: a #HildonTouchSelector
1360  * @column: the column number we want to get the element
1361  * @iter: #GtkTreeIter currently selected
1362  *
1363  * Sets @iter to the currently selected node on the nth-column, if selection is
1364  * set to %HILDON_TOUCH_SELECTOR_SINGLE or %HILDON_TOUCH_SELECTOR_MULTIPLE with
1365  * a column different that the first one. @iter may be %NULL if you just want to
1366  * test if selection has any selected items.
1367  *
1368  * This function will not work if selection is in
1369  * %HILDON_TOUCH_SELECTOR_MULTIPLE mode and the column is the first one.
1370  *
1371  * See gtk_tree_selection_get_selected() for more information.
1372  *
1373  * Returns: %TRUE if @iter was correctly set, %FALSE otherwise
1374  **/
1375 gboolean
1376 hildon_touch_selector_get_selected (HildonTouchSelector * selector,
1377                                     gint column, GtkTreeIter * iter)
1378 {
1379   GtkTreeSelection *selection = NULL;
1380   HildonTouchSelectorColumn *current_column = NULL;
1381   HildonTouchSelectorSelectionMode mode;
1382
1383   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
1384   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
1385                         FALSE);
1386   mode = hildon_touch_selector_get_column_selection_mode (selector);
1387   g_return_val_if_fail
1388     ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE) ||
1389      ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)&&(column>0)),
1390      FALSE);
1391
1392   current_column = g_slist_nth_data (selector->priv->columns, column);
1393
1394   selection =
1395     gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
1396
1397   return gtk_tree_selection_get_selected (selection, NULL, iter);
1398 }
1399
1400 /**
1401  * hildon_touch_selector_select_iter
1402  * @selector: a #HildonTouchSelector
1403  * @column:   the column to selects
1404  * @iter:     the #GtkTreeIter to be selected
1405  * @scroll_to: whether to smoothly scroll to the item
1406  *
1407  * Sets the currently selected item in the column @column to the one pointed by @iter,
1408  * optionally smoothly scrolling to it.
1409  *
1410  **/
1411 void
1412 hildon_touch_selector_select_iter (HildonTouchSelector * selector,
1413                                    gint column, GtkTreeIter * iter,
1414                                    gboolean scroll_to)
1415 {
1416   GtkTreePath *path;
1417   GtkTreeModel *model;
1418   GdkRectangle rect;
1419   HildonTouchSelectorColumn *current_column = NULL;
1420   GtkTreeView *tv = NULL;
1421   GtkTreeSelection *selection = NULL;
1422   gint y;
1423
1424   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1425   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
1426
1427   current_column = g_slist_nth_data (selector->priv->columns, column);
1428
1429   tv = current_column->priv->tree_view;
1430   selection = gtk_tree_view_get_selection (tv);
1431   model = gtk_tree_view_get_model (tv);
1432   path = gtk_tree_model_get_path (model, iter);
1433
1434   gtk_tree_selection_select_iter (selection, iter);
1435
1436   if (scroll_to) {
1437     gtk_tree_view_get_background_area (tv,
1438                                        path, NULL, &rect);
1439     gtk_tree_view_convert_bin_window_to_tree_coords (tv,
1440                                                      0, rect.y, NULL, &y);
1441     hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA (current_column->priv->panarea),
1442                                     -1, y);
1443   }
1444   gtk_tree_path_free (path);
1445 }
1446
1447 /**
1448  * hildon_touch_selector_unselect_iter
1449  * @selector: a #HildonTouchSelector
1450  * @column:   the column to unselects from
1451  * @iter:     the #GtkTreeIter to be unselected
1452  *
1453  * Unselect the item pointed by @iter in the column @column
1454  *
1455  **/
1456
1457 void hildon_touch_selector_unselect_iter (HildonTouchSelector * selector,
1458                                           gint column,
1459                                           GtkTreeIter * iter)
1460 {
1461   HildonTouchSelectorColumn *current_column = NULL;
1462   GtkTreeSelection *selection = NULL;
1463
1464   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1465   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
1466
1467   current_column = g_slist_nth_data (selector->priv->columns, column);
1468   selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
1469   gtk_tree_selection_unselect_iter (selection, iter);
1470 }
1471
1472 /**
1473  * hildon_touch_selector_get_selected_rows:
1474  * @selector: a #HildonTouchSelector
1475  * @column: the position of the column to get the selected rows from
1476  *
1477  * Creates a list of #GtkTreePath<!-- -->s of all selected rows in a column. Additionally,
1478  * if you to plan to modify the model after calling this function, you may
1479  * want to convert the returned list into a list of GtkTreeRowReferences. To do this,
1480  * you can use gtk_tree_row_reference_new().
1481  *
1482  * See gtk_tree_selection_get_selected_rows() for more information.
1483  *
1484  * Returns: A new #GList containing a #GtkTreePath for each selected row in the column @column.
1485  *
1486  **/
1487 GList *
1488 hildon_touch_selector_get_selected_rows (HildonTouchSelector * selector,
1489                                          gint column)
1490 {
1491   GList *result = NULL;
1492   HildonTouchSelectorColumn *current_column = NULL;
1493   GtkTreeSelection *selection = NULL;
1494
1495   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1496   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
1497                         NULL);
1498
1499   current_column = g_slist_nth_data (selector->priv->columns, column);
1500   selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
1501
1502   result = gtk_tree_selection_get_selected_rows (selection, NULL);
1503
1504   return result;
1505 }
1506
1507 /**
1508  * hildon_touch_selector_get_model:
1509  * @selector: a #HildonTouchSelector
1510  * @column: the position of the column in @selector
1511  *
1512  * Gets the model of a column of @selector.
1513  *
1514  * Returns: the #GtkTreeModel for the column @column of @selector.
1515  **/
1516 GtkTreeModel *
1517 hildon_touch_selector_get_model (HildonTouchSelector * selector, gint column)
1518 {
1519   HildonTouchSelectorColumn *current_column = NULL;
1520
1521   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1522   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
1523                         NULL);
1524
1525   current_column = g_slist_nth_data (selector->priv->columns, column);
1526
1527   return current_column->priv->model;
1528 }
1529
1530 static void
1531 _hildon_touch_selector_set_model (HildonTouchSelector * selector,
1532                                   gint column, GtkTreeModel * model)
1533 {
1534   HildonTouchSelectorColumn *current_column = NULL;
1535
1536   current_column =
1537     HILDON_TOUCH_SELECTOR_COLUMN (g_slist_nth_data (selector->priv->columns, column));
1538
1539   current_column->priv->model = model;
1540   gtk_tree_view_set_model (current_column->priv->tree_view,
1541                            current_column->priv->model);
1542 }
1543
1544 /**
1545  * hildon_touch_selector_set_model:
1546  * @selector: a #HildonTouchSelector
1547  * @column: the position of the column to set the model to
1548  * @model: a #GtkTreeModel
1549  *
1550  * Sets the #GtkTreeModel for a particular column in @model.
1551  **/
1552 void
1553 hildon_touch_selector_set_model (HildonTouchSelector * selector,
1554                                  gint column, GtkTreeModel * model)
1555 {
1556   g_return_if_fail (HILDON_TOUCH_SELECTOR (selector));
1557   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
1558
1559   HILDON_TOUCH_SELECTOR_GET_CLASS (selector)->set_model (selector, column, model);
1560 }
1561
1562 /**
1563  * hildon_touch_selector_get_current_text:
1564  * @selector: a #HildonTouchSelector
1565  *
1566  * Returns a string representing the currently selected items for
1567  * each column of @selector. See hildon_touch_selector_set_print_func().
1568  *
1569  * Returns: a newly allocated string.
1570  **/
1571 gchar *
1572 hildon_touch_selector_get_current_text (HildonTouchSelector * selector)
1573 {
1574   gchar *result = NULL;
1575   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1576
1577   if (selector->priv->print_func) {
1578     result = (*selector->priv->print_func) (selector);
1579   } else {
1580     result = _default_print_func (selector);
1581   }
1582
1583   return result;
1584 }
1585
1586 static gboolean
1587 _hildon_touch_selector_center_on_selected_items (GtkWidget *widget,
1588                                                  gpointer data)
1589 {
1590   HildonTouchSelector *selector = NULL;
1591   HildonTouchSelectorColumn *column = NULL;
1592   GtkTreeIter iter;
1593   GtkTreePath *path = NULL;
1594   GdkRectangle rect;
1595   gint y;
1596   gint num_column = -1;
1597   HildonTouchSelectorSelectionMode selection_mode;
1598
1599   column = HILDON_TOUCH_SELECTOR_COLUMN (data);
1600   selector = column->priv->parent;
1601
1602   if (!selector->priv->initial_scroll) {
1603     return FALSE;
1604   }
1605   selection_mode = hildon_touch_selector_get_column_selection_mode (selector);
1606   num_column = g_slist_index (selector->priv->columns, column);
1607
1608   if ((num_column == 0)
1609       && (selection_mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)) {
1610
1611     return FALSE;
1612   }
1613
1614   if (hildon_touch_selector_get_selected (selector, num_column, &iter)) {
1615     path = gtk_tree_model_get_path (column->priv->model, &iter);
1616     gtk_tree_view_get_background_area (GTK_TREE_VIEW
1617                                        (column->priv->tree_view), path, NULL,
1618                                        &rect);
1619
1620     gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW
1621                                                      (column->priv->tree_view), 0,
1622                                                      rect.y, NULL, &y);
1623
1624     hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA
1625                                     (column->priv->panarea), -1, y);
1626
1627     gtk_tree_path_free (path);
1628   }
1629
1630   return FALSE;
1631 }
1632
1633 static gboolean
1634 _hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
1635 {
1636   HildonTouchSelectorSelectionMode mode = HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
1637   gint n_columns = 0;
1638
1639   n_columns = hildon_touch_selector_get_num_columns (selector);
1640   mode = hildon_touch_selector_get_column_selection_mode (selector);
1641
1642   return ((n_columns > 1) || (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE));
1643 }
1644
1645 /**
1646  * hildon_touch_selector_has_multiple_selection:
1647  * @selector: A #HildonTouchSelector
1648  *
1649  * Determines whether @selector is complex enough to actually require an
1650  * extra selection step than only picking an item. This is normally %TRUE
1651  * if @selector has multiple columns, multiple selection, or when it is a
1652  * more complex widget, like #HildonTouchSelectorEntry.
1653  *
1654  * This information is useful for widgets containing a #HildonTouchSelector,
1655  * like #HildonPickerDialog, that could need a "Done" button, in case that
1656  * its internal #HildonTouchSelector has multiple columns, for instance.
1657  *
1658  * Returns: %TRUE if @selector requires multiple selection steps.
1659  **/
1660 gboolean
1661 hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
1662 {
1663   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
1664
1665   return HILDON_TOUCH_SELECTOR_GET_CLASS (selector)->has_multiple_selection (selector);
1666 }
1667
1668
1669 /**
1670  * hildon_touch_selector_get_column:
1671  * @selector: A #HildonTouchSelector
1672  * @column: a column number
1673  *
1674  * Use this method to retrieve a #HildonTouchSelectorColumn. Then, you can use
1675  * the #GtkCellLayout interface to set up the layout of the column.
1676  *
1677  * Returns: the @column<!-- -->-th #HildonTouchSelectorColumn in @selector
1678  **/
1679 HildonTouchSelectorColumn *
1680 hildon_touch_selector_get_column (HildonTouchSelector * selector,
1681                                   gint column)
1682 {
1683   gint num_columns = -1;
1684
1685   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1686   num_columns = hildon_touch_selector_get_num_columns (selector);
1687   g_return_val_if_fail (column < num_columns && column >= 0, NULL);
1688
1689   return g_slist_nth_data (selector->priv->columns, column);
1690 }