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