2008-08-04 Claudio Saavedra <csaavedra@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, very similar to the #GtkComboBox, but with
26  * several individual pannable columns
27  *
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <string.h>
35 #include <stdlib.h>
36 #include "hildon-pannable-area.h"
37 #include "hildon-touch-selector.h"
38
39 #define HILDON_TOUCH_SELECTOR_GET_PRIVATE(obj)                          \
40   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_TOUCH_SELECTOR, HildonTouchSelectorPrivate))
41
42 G_DEFINE_TYPE (HildonTouchSelector, hildon_touch_selector, GTK_TYPE_VBOX)
43
44 #define CENTER_ON_SELECTED_ITEM_DELAY 50
45
46 /**
47  * Struct to maintain the data of each column. The columns are the elements
48  * of the widget that belongs properly to the selection behaviour. As
49  * the selector contents are arranged in a #GtkHBox, you can add more widgets, like buttons etc.
50  * between the columns, but this doesn't belongs to the selection
51  * logic
52  */
53 typedef struct _SelectorColumn SelectorColumn;
54 struct _SelectorColumn
55 {
56   HildonTouchSelector *parent;    /* the selector that contains this column */
57   GtkTreeModel *model;
58   GtkTreeView *tree_view;
59
60   GtkWidget *panarea;           /* the pannable widget */
61 };
62
63 struct _HildonTouchSelectorPrivate
64 {
65   GSList *columns;              /* the selection columns */
66   GtkWidget *hbox;              /* the container for the selector's columns */
67
68   HildonTouchSelectorPrintFunc print_func;
69 };
70
71 enum
72 {
73   PROP_HAS_MULTIPLE_SELECTION = 1
74 };
75
76 enum
77 {
78   CHANGED,
79   LAST_SIGNAL
80 };
81
82 static gint hildon_touch_selector_signals[LAST_SIGNAL] = { 0 };
83
84 static void hildon_touch_selector_get_property (GObject * object,
85                                                 guint prop_id,
86                                                 GValue * value, GParamSpec * pspec);
87
88 /* gtkwidget */
89 static void hildon_touch_selector_map (GtkWidget * widget);
90
91 /* gtkcontainer */
92 static void hildon_touch_selector_remove (GtkContainer * container,
93                                           GtkWidget * widget);
94 /* private functions */
95 static void _selection_changed_cb (GtkTreeSelection * selection,
96                                    gpointer user_data);
97 static gchar *_default_print_func (HildonTouchSelector * selector);
98
99 static SelectorColumn *_create_new_column (HildonTouchSelector * selector,
100                                            GtkTreeModel * model,
101                                            GtkCellRenderer * renderer,
102                                            va_list args);
103 static gboolean _hildon_touch_selector_center_on_selected_items (gpointer data);
104
105 static void
106 _hildon_touch_selector_set_model (HildonTouchSelector * selector,
107                                   gint num_column, GtkTreeModel * model);
108 static gboolean
109 _hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector);
110
111 static void
112 hildon_touch_selector_class_init (HildonTouchSelectorClass * class)
113 {
114   GObjectClass *gobject_class;
115   GtkObjectClass *object_class;
116   GtkWidgetClass *widget_class;
117   GtkContainerClass *container_class;
118
119   gobject_class = (GObjectClass *) class;
120   object_class = (GtkObjectClass *) class;
121   widget_class = (GtkWidgetClass *) class;
122   container_class = (GtkContainerClass *) class;
123
124   /* GObject */
125
126   gobject_class->get_property = hildon_touch_selector_get_property;
127
128   /* GtkWidget */
129   widget_class->map = hildon_touch_selector_map;
130
131   /* GtkContainer */
132   container_class->remove = hildon_touch_selector_remove;
133
134   /* HildonTouchSelector */
135   class->set_model = _hildon_touch_selector_set_model;
136
137   class->has_multiple_selection = _hildon_touch_selector_has_multiple_selection;
138
139   /* signals */
140   /**
141    * HildonTouchSelector::changed:
142    * @widget: the object which received the signal
143    *
144    * The changed signal is emitted when the active
145    * item is changed. The can be due to the user selecting
146    * a different item from the list, or due to a
147    * call to hildon_touch_selector_set_active_iter() on
148    * one of the columns
149    *
150    */
151   hildon_touch_selector_signals[CHANGED] =
152     g_signal_new ("changed",
153                   G_OBJECT_CLASS_TYPE (class),
154                   G_SIGNAL_RUN_LAST,
155                   G_STRUCT_OFFSET (HildonTouchSelectorClass, changed),
156                   NULL, NULL,
157                   gtk_marshal_NONE__INT, G_TYPE_NONE, 1, G_TYPE_INT);
158   /* properties */
159
160   g_object_class_install_property (gobject_class, PROP_HAS_MULTIPLE_SELECTION,
161                                    g_param_spec_boolean ("has-multiple-selection",
162                                                          "has multiple selection",
163                                                          "Whether the widget has multiple "
164                                                          "selection (like multiple columns, "
165                                                          "multiselection mode, or multiple "
166                                                          "internal widgets) and therefore "
167                                                          "it may need a confirmation button, "
168                                                          "for instance.",
169                                                          FALSE,
170                                                          G_PARAM_READABLE));
171
172   /* style properties */
173   g_type_class_add_private (object_class, sizeof (HildonTouchSelectorPrivate));
174 }
175
176 static void
177 hildon_touch_selector_get_property (GObject * object,
178                                     guint prop_id,
179                                     GValue * value, GParamSpec * pspec)
180 {
181   switch (prop_id) {
182   case PROP_HAS_MULTIPLE_SELECTION:
183     g_value_set_boolean (value,
184                          hildon_touch_selector_has_multiple_selection (HILDON_TOUCH_SELECTOR (object)));
185     break;
186   default:
187     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188     break;
189   }
190 }
191
192 static void
193 hildon_touch_selector_init (HildonTouchSelector * selector)
194 {
195   selector->priv = HILDON_TOUCH_SELECTOR_GET_PRIVATE (selector);
196
197   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (selector), GTK_NO_WINDOW);
198   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (selector), FALSE);
199
200   selector->priv->columns = NULL;
201
202   selector->priv->print_func = NULL;
203   selector->priv->hbox = gtk_hbox_new (FALSE, 0);
204
205   gtk_box_pack_end (GTK_BOX (selector), selector->priv->hbox,
206                     TRUE, TRUE, 0);
207   gtk_widget_show (selector->priv->hbox);
208
209   /* FIXME: this is the correct height? A fixed height is the correct 
210      implementation */
211   gtk_widget_set_size_request (GTK_WIDGET (selector), -1, 320);
212 }
213
214 static void
215 hildon_touch_selector_map (GtkWidget * widget)
216 {
217   GTK_WIDGET_CLASS (hildon_touch_selector_parent_class)->map (widget);
218
219   g_timeout_add (CENTER_ON_SELECTED_ITEM_DELAY,
220                  _hildon_touch_selector_center_on_selected_items, widget);
221 }
222
223 /*------------------------------ GtkContainer ------------------------------ */
224
225 /*
226  * Required in order to free the column at the columns list
227  */
228 static void
229 hildon_touch_selector_remove (GtkContainer * container, GtkWidget * widget)
230 {
231   HildonTouchSelector *selector = NULL;
232   GSList *iter = NULL;
233   gint position = 0;
234   SelectorColumn *current_column = NULL;
235   gint num_columns = 0;
236
237   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (container));
238
239   selector = HILDON_TOUCH_SELECTOR (container);
240   num_columns = hildon_touch_selector_get_num_columns (selector);
241
242   /* Check if the widget is inside a column and remove
243      it from the list */
244   iter = selector->priv->columns;
245   position = 0;
246   while (iter) {
247     current_column = (SelectorColumn *) iter->data;
248     if (widget == current_column->panarea) {
249       current_column = g_slist_nth_data (selector->priv->columns, position);
250
251       selector->priv->columns = g_slist_remove (selector->priv->columns,
252                                                 current_column);
253       g_free (current_column);
254
255       break;
256     }
257
258     position++;
259     iter = g_slist_next (iter);
260   }
261   if (position >= num_columns) {
262     g_debug ("This widget was not inside the selector column");
263   }
264
265   GTK_CONTAINER_CLASS (hildon_touch_selector_parent_class)->remove (container, widget);
266 }
267
268 /* ------------------------------ PRIVATE METHODS ---------------------------- */
269 /**
270  * default_print_func:
271  * @selector: a #HildonTouchSelector
272  *
273  * Default print function
274  *
275  * Returns: a new string that represent the selected items
276  **/
277 static gchar *
278 _default_print_func (HildonTouchSelector * selector)
279 {
280   gchar *result = NULL;
281   gchar *aux = NULL;
282   gint num_columns = 0;
283   GtkTreeIter iter;
284   GtkTreeModel *model = NULL;
285   gchar *current_string = NULL;
286   gint i;
287   HildonTouchSelectorSelectionMode mode;
288   GList *item = NULL;
289   GtkTreePath *current_path = NULL;
290   GList *selected_rows = NULL;
291   gint initial_value = 0;
292
293   num_columns = hildon_touch_selector_get_num_columns (selector);
294
295   mode = hildon_touch_selector_get_column_selection_mode (selector);
296
297   if ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
298       && (num_columns > 0)) {
299     /* In this case we get the first column first */
300     selected_rows = hildon_touch_selector_get_selected_rows (selector, 0);
301     model = hildon_touch_selector_get_model (selector, 0);
302
303     result = g_strdup_printf ("(");
304     i = 0;
305     for (item = selected_rows; item; item = g_list_next (item)) {
306       current_path = item->data;
307       gtk_tree_model_get_iter (model, &iter, current_path);
308
309       gtk_tree_model_get (model, &iter, 0, &current_string, -1);
310
311       if (i < g_list_length (selected_rows) - 1) {
312         aux = g_strconcat (result, current_string, ",", NULL);
313         g_free (result);
314         result = aux;
315       } else {
316         aux = g_strconcat (result, current_string, NULL);
317         g_free (result);
318         result = aux;
319       }
320       i++;
321     }
322
323     aux = g_strconcat (result, ")", NULL);
324     g_free (result);
325     result = aux;
326
327     g_list_foreach (selected_rows, (GFunc) (gtk_tree_path_free), NULL);
328     g_list_free (selected_rows);
329     initial_value = 1;
330   } else {
331     initial_value = 0;
332   }
333
334   for (i = initial_value; i < num_columns; i++) {
335     model = hildon_touch_selector_get_model (selector, i);
336     if (hildon_touch_selector_get_active_iter (selector, i, &iter)) {
337
338       gtk_tree_model_get (model, &iter, 0, &current_string, -1);
339       if (i != 0) {
340         aux = g_strconcat (result, ":", current_string, NULL);
341         g_free (result);
342         result = aux;
343       } else {
344         result = g_strdup_printf ("%s", current_string);
345       }
346     }
347   }
348
349   return result;
350 }
351
352 static void
353 _selection_changed_cb (GtkTreeSelection * selection, gpointer user_data)
354 {
355   HildonTouchSelector *selector = NULL;
356   SelectorColumn *column = NULL;
357   gint num_column = -1;
358
359   column = (SelectorColumn *) user_data;
360   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (column->parent));
361
362   selector = column->parent;
363
364   num_column = g_slist_index (selector->priv->columns, column);
365
366   g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, num_column);
367 }
368
369
370 static SelectorColumn *
371 _create_new_column (HildonTouchSelector * selector,
372                     GtkTreeModel * model,
373                     GtkCellRenderer * renderer, va_list args)
374 {
375   SelectorColumn *new_column = NULL;
376   GtkTreeViewColumn *tree_column = NULL;
377   GValue val = { 0, };
378   GtkTreeView *tv = NULL;
379   GtkWidget *panarea = NULL;
380   GtkTreeSelection *selection = NULL;
381   GtkTreeIter iter;
382   gchar *attribute;
383   gint value;
384
385   tree_column = gtk_tree_view_column_new ();
386   gtk_tree_view_column_pack_start (tree_column, renderer, TRUE);
387
388   attribute = va_arg (args, gchar *);
389   while (attribute != NULL) {
390     value = va_arg (args, gint);
391     gtk_tree_view_column_add_attribute (tree_column, renderer, attribute,
392                                         value);
393     attribute = va_arg (args, gchar *);
394   }
395
396   tv = g_object_new (GTK_TYPE_TREE_VIEW, "model", model, "name", "fremantle-widget",    /* FIXME: is this always this name? */
397                      "hildon-ui-mode", HILDON_UI_MODE_EDIT,
398                      "rules-hint", TRUE, NULL);
399
400   gtk_tree_view_append_column (GTK_TREE_VIEW (tv), tree_column);
401
402   new_column = (SelectorColumn *) g_malloc0 (sizeof (SelectorColumn));
403   new_column->parent = selector;
404
405   panarea = hildon_pannable_area_new ();
406
407   g_value_init (&val, G_TYPE_INT);
408   g_value_set_int (&val, HILDON_PANNABLE_AREA_INDICATOR_MODE_HIDE);
409   g_object_set_property (G_OBJECT (panarea), "vindicator-mode", &val);
410
411   g_value_unset (&val);
412   g_value_init (&val, G_TYPE_BOOLEAN);
413   g_value_set_boolean (&val, FALSE);
414   g_object_set_property (G_OBJECT (panarea), "initial-hint", &val);
415
416   gtk_container_add (GTK_CONTAINER (panarea), GTK_WIDGET (tv));
417
418   new_column->model = model;
419   new_column->tree_view = tv;
420   new_column->panarea = panarea;
421
422   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
423   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
424
425   /* select the first item */
426   if (gtk_tree_model_get_iter_first (model, &iter)) {
427     gtk_tree_selection_select_iter (selection, &iter);
428   }
429
430   gtk_widget_grab_focus (GTK_WIDGET (tv));
431
432   /* connect to the changed signal connection */
433   g_signal_connect (G_OBJECT (selection), "changed",
434                     G_CALLBACK (_selection_changed_cb), new_column);
435
436   return new_column;
437 }
438
439 /* ------------------------------ PUBLIC METHODS ---------------------------- */
440
441 /**
442  * hildon_touch_selector_new:
443  * @:
444  *
445  * Creates a new empty #HildonTouchSelector
446  *
447  * Returns: a new #HildonTouchSelector
448  **/
449 GtkWidget *
450 hildon_touch_selector_new ()
451 {
452   return g_object_new (HILDON_TYPE_TOUCH_SELECTOR, NULL);
453 }
454
455 /**
456  * hildon_touch_selector_new_text:
457  * @void: 
458  *
459  * Creates a #HildonTouchSelector with a single text column that
460  * can be populated conveniently through hildon_touch_selector_append_text(),
461  * hildon_touch_selector_prepend_text(), hildon_touch_selector_insert_text().
462  *
463  * Returns: A new #HildonTouchSelector
464  **/
465 GtkWidget *
466 hildon_touch_selector_new_text (void)
467 {
468   GtkWidget *selector;
469   GtkListStore *store;
470
471   selector = hildon_touch_selector_new ();
472   store = gtk_list_store_new (1, G_TYPE_STRING);
473
474   hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
475                                             GTK_TREE_MODEL (store));
476
477   return selector;
478 }
479
480 /**
481  * hildon_touch_selector_append_text:
482  * @selector: A #HildonTouchSelector.
483  * @text: a non %NULL text string.
484  *
485  * Appends a new entry in a #HildonTouchSelector created with
486  * hildon_touch_selector_new_text().
487  **/
488 void
489 hildon_touch_selector_append_text (HildonTouchSelector * selector,
490                                    const gchar * text)
491 {
492   GtkTreeIter iter;
493   GtkTreeModel *model;
494
495   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
496   g_return_if_fail (text != NULL);
497
498   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
499
500   g_return_if_fail (GTK_IS_LIST_STORE (model));
501
502   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
503   gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, text, -1);
504 }
505
506 /**
507  * hildon_touch_selector_prepend_text:
508  * @selector: A #HildonTouchSelector.
509  * @text: a non %NULL text string.
510  *
511  * Prepends a new entry in a #HildonTouchSelector created with
512  * hildon_touch_selector_new_text().
513  **/
514 void
515 hildon_touch_selector_prepend_text (HildonTouchSelector * selector,
516                                     const gchar * text)
517 {
518   GtkTreeIter iter;
519   GtkTreeModel *model;
520
521   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
522   g_return_if_fail (text != NULL);
523
524   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
525
526   g_return_if_fail (GTK_IS_LIST_STORE (model));
527
528   gtk_list_store_prepend (GTK_LIST_STORE (model), &iter);
529   gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, text, -1);
530 }
531
532 /**
533  * hildon_touch_selector_insert_text:
534  * @selector: a #HildonTouchSelector.
535  * @position: the position to insert @text.
536  * @text: A non %NULL text string.
537  *
538  * Inserts a new entry in particular positio of a #HildoTouchSelector created
539  * with hildon_touch_selector_new_text().
540  *
541  **/
542 void
543 hildon_touch_selector_insert_text (HildonTouchSelector * selector,
544                                    gint position, const gchar * text)
545 {
546   GtkTreeIter iter;
547   GtkTreeModel *model;
548
549   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
550   g_return_if_fail (text != NULL);
551   g_return_if_fail (position >= 0);
552
553   model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
554
555   g_return_if_fail (GTK_IS_LIST_STORE (model));
556
557   gtk_list_store_insert (GTK_LIST_STORE (model), &iter, position);
558   gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, text, -1);
559 }
560
561 /**
562  * hildon_touch_selector_append_text_column
563  * @selector: the #HildonTouchSelector widget
564  * @model: the #GtkTreeModel with the data of the column
565  *
566  * This functions adds a new column to the widget, with the data on
567  * the model. Only the widgets added in this way should used on
568  * the selection logic, ie: the print function, the "changed" signal etc.
569  *
570  * There are a prerequisite on this model: this append
571  * consideres that the text data is in the first column of the model
572  *
573  * Basically it adds a tree view to the widget, using the model and
574  * the data received.
575  *
576  * Returns: TRUE if a new column were added, FALSE otherside
577  **/
578 gboolean
579 hildon_touch_selector_append_column (HildonTouchSelector * selector,
580                                      GtkTreeModel * model,
581                                      GtkCellRenderer * cell_renderer, ...)
582 {
583   va_list args;
584   SelectorColumn *new_column = NULL;
585
586   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
587   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
588
589   if (model != NULL) {
590
591     va_start (args, cell_renderer);
592     new_column = _create_new_column (selector, model, cell_renderer, args);
593     va_end (args);
594
595     selector->priv->columns = g_slist_append (selector->priv->columns,
596                                               new_column);
597     gtk_box_pack_start (GTK_BOX (selector->priv->hbox), new_column->panarea, TRUE, TRUE, 6);
598
599     gtk_widget_show_all (new_column->panarea);
600   } else {
601     return FALSE;
602   }
603
604   return TRUE;
605 }
606
607 /**
608  * hildon_touch_selector_append_text_column
609  * @selector: the #HildonTouchSelector widget
610  * @model: the #GtkTreeModel with the data of the column
611  *
612  * Equivalent to hildon_touch_selector_append_column, but using a
613  * default text cell renderer. This is the most common use of the
614  * widget.
615  *
616  * Returns: TRUE if a new column were added, FALSE otherside
617  **/
618 gboolean
619 hildon_touch_selector_append_text_column (HildonTouchSelector * selector,
620                                           GtkTreeModel * model)
621 {
622   GtkCellRenderer *renderer = NULL;
623   GValue val = { 0, };
624
625   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
626   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
627
628   if (model != NULL) {
629     renderer = gtk_cell_renderer_text_new ();
630
631     g_value_init (&val, G_TYPE_FLOAT);
632     g_value_set_float (&val, 0.5);
633     /* FIXME: center the text, this should be configurable */
634     g_object_set_property (G_OBJECT (renderer), "xalign", &val);
635
636     return hildon_touch_selector_append_column (selector, model, renderer,
637                                                 "text", 0, NULL);
638   } else {
639     return FALSE;
640   }
641 }
642
643 /**
644  * hildon_touch_selector_remove_column
645  * @selector: a #HildonTouchSelector
646  * @position: the column position to remove, counting from 0 to (total column number - 1)
647  *
648  *
649  * Returns: TRUE is the column was removed, FALSE otherwise
650  **/
651 gboolean
652 hildon_touch_selector_remove_column (HildonTouchSelector * selector, gint position)
653 {
654   SelectorColumn *current_column = NULL;
655
656   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
657   g_return_val_if_fail (position <
658                         hildon_touch_selector_get_num_columns (selector), FALSE);
659
660   current_column = g_slist_nth_data (selector->priv->columns, position);
661
662   gtk_container_remove (GTK_CONTAINER (selector), current_column->panarea);
663
664   return TRUE;
665 }
666
667 void
668 hildon_touch_selector_set_column_attributes (HildonTouchSelector * selector,
669                                              gint num_column,
670                                              GtkCellRenderer * cell_renderer,
671                                              ...)
672 {
673   va_list args;
674   GtkTreeViewColumn *tree_column = NULL;
675   SelectorColumn *current_column = NULL;
676   gchar *attribute = NULL;
677   gint value = 0;
678
679   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
680   g_return_if_fail (num_column <
681                     hildon_touch_selector_get_num_columns (selector));
682
683   current_column = g_slist_nth_data (selector->priv->columns, num_column);
684
685   tree_column = gtk_tree_view_get_column (current_column->tree_view, 0);
686   gtk_tree_view_remove_column (current_column->tree_view, tree_column);
687
688   tree_column = gtk_tree_view_column_new ();
689   gtk_tree_view_column_pack_start (tree_column, cell_renderer, TRUE);
690
691   va_start (args, cell_renderer);
692   attribute = va_arg (args, gchar *);
693
694   gtk_tree_view_column_clear_attributes (tree_column, cell_renderer);
695
696   while (attribute != NULL) {
697     value = va_arg (args, gint);
698     gtk_tree_view_column_add_attribute (tree_column, cell_renderer,
699                                         attribute, value);
700     attribute = va_arg (args, gchar *);
701   }
702
703   va_end (args);
704
705   gtk_tree_view_append_column (current_column->tree_view, tree_column);
706 }
707
708 gboolean
709 hildon_touch_selector_insert_column (HildonTouchSelector * selector, gint position)
710 {
711   g_warning ("Un-implemented!");
712
713   return TRUE;
714 }
715
716 gint
717 hildon_touch_selector_get_num_columns (HildonTouchSelector * selector)
718 {
719   return g_slist_length (selector->priv->columns);
720 }
721
722 HildonTouchSelectorSelectionMode
723 hildon_touch_selector_get_column_selection_mode (HildonTouchSelector * selector)
724 {
725   HildonTouchSelectorSelectionMode result =
726     HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
727   GtkSelectionMode treeview_mode = GTK_SELECTION_SINGLE;
728   SelectorColumn *column = NULL;
729   GtkTreeSelection *selection = NULL;
730
731   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), result);
732   g_return_val_if_fail (hildon_touch_selector_get_num_columns (selector) > 0,
733                         result);
734
735   column = (SelectorColumn *) selector->priv->columns->data;
736
737   selection = gtk_tree_view_get_selection (column->tree_view);
738   treeview_mode = gtk_tree_selection_get_mode (selection);
739
740
741   if (treeview_mode == GTK_SELECTION_MULTIPLE) {
742     result = HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE;
743   } else {
744     result = HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
745   }
746
747   return result;
748 }
749
750 void
751 hildon_touch_selector_set_column_selection_mode (HildonTouchSelector * selector,
752                                                  HildonTouchSelectorSelectionMode
753                                                  mode)
754 {
755   GtkTreeView *tv = NULL;
756   SelectorColumn *column = NULL;
757   GtkTreeSelection *selection = NULL;
758   GtkSelectionMode treeview_mode;
759   GtkTreeIter iter;
760
761   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
762   g_return_if_fail (hildon_touch_selector_get_num_columns (selector) > 0);
763
764   column = (SelectorColumn *) (g_slist_nth (selector->priv->columns, 0))->data;
765   tv = column->tree_view;
766
767   if (tv) {
768     switch (mode) {
769     case HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE:
770       treeview_mode = GTK_SELECTION_SINGLE;
771       break;
772     case HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE:
773       treeview_mode = GTK_SELECTION_MULTIPLE;
774       break;
775     }
776
777     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
778     gtk_tree_selection_set_mode (selection, treeview_mode);
779
780     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
781     gtk_tree_model_get_iter_first (column->model, &iter);
782     gtk_tree_selection_unselect_all (selection);
783     gtk_tree_selection_select_iter (selection, &iter);
784   }
785
786 }
787
788 void
789 hildon_touch_selector_set_print_func (HildonTouchSelector * selector,
790                                       HildonTouchSelectorPrintFunc func)
791 {
792   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
793
794   selector->priv->print_func = func;
795 }
796
797 HildonTouchSelectorPrintFunc
798 hildon_touch_selector_get_print_func (HildonTouchSelector * selector)
799 {
800   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
801
802   return selector->priv->print_func;
803 }
804
805 /**
806  * hildon_touch_selector_get_active_iter:
807  * @selector: a #HildonTouchSelector
808  * @column: the column number we want to get the element
809  * @iter: #GtkTreeIter currently selected
810  *
811  * Sets iter to the currently selected node on the nth-column, if selection is set to
812  * HILDON_TOUCH_SELECTOR_SINGLE. iter may be NULL if you just want to test if selection
813  * has any selected nodes.
814  *
815  * This function will not work if you use selection is HILDON_TOUCH_SELECTOR_MULTIPLE.
816  *
817  * See gtk_tree_selection_get_selected for more information
818  *
819  * Returns: TRUE if was posible to get the iter, FALSE otherwise
820  **/
821 gboolean
822 hildon_touch_selector_get_active_iter (HildonTouchSelector * selector,
823                                        gint column, GtkTreeIter * iter)
824 {
825   GtkTreeSelection *selection = NULL;
826   SelectorColumn *current_column = NULL;
827
828   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
829   g_return_val_if_fail (hildon_touch_selector_get_column_selection_mode (selector)
830                         == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE, FALSE);
831   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
832                         FALSE);
833
834   current_column = g_slist_nth_data (selector->priv->columns, column);
835
836   selection =
837     gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->tree_view));
838
839   return gtk_tree_selection_get_selected (selection, NULL, iter);
840 }
841
842 /**
843  * hildon_touch_selector_set_active_iter
844  * @selector: a #HildonTouchSelector
845  * @column:   the column to selects
846  * @iter:     the #GtkTreeIter to be selected
847  *
848  * Sets the current iter
849  *
850  **/
851 void
852 hildon_touch_selector_set_active_iter (HildonTouchSelector * selector,
853                                        gint column, GtkTreeIter * iter,
854                                        gboolean scroll_to)
855 {
856   GtkTreePath *path;
857   GtkTreeModel *model;
858   GdkRectangle rect;
859   SelectorColumn *current_column = NULL;
860   GtkTreeSelection *selection = NULL;
861   gint y;
862
863   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
864   g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
865
866   current_column = g_slist_nth_data (selector->priv->columns, column);
867
868   selection = gtk_tree_view_get_selection (current_column->tree_view);
869
870   gtk_tree_selection_select_iter (selection, iter);
871
872   if (scroll_to) {
873     model = gtk_tree_view_get_model (current_column->tree_view);
874     path = gtk_tree_model_get_path (model, iter);
875     gtk_tree_view_get_background_area (current_column->tree_view,
876                                        path, NULL, &rect);
877     gtk_tree_view_convert_bin_window_to_tree_coords (current_column->tree_view,
878                                                      0, rect.y, NULL, &y);
879     hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA (current_column->panarea),
880                                     -1, y);
881     gtk_tree_path_free (path);
882   }
883 }
884
885 /**
886  * hildon_touch_selector_get_selected_rows:
887  * @selector: a #HildonTouchSelector
888  * @column:
889  *
890  * Creates a list of path of all selected rows at a concrete column. Additionally,
891  * if you are planning on modifying the model after calling this function, you may
892  * want to convert the returned list into a list of GtkTreeRowReferences. To do this,
893  * you can use gtk_tree_row_reference_new().
894  *
895  * See #GtkTreeSelection:gtk_tree_selection_get_selected_rows for more information
896  *
897  * Returns: A new GList containing a GtkTreePath for each selected row in the concrete column
898  *
899  **/
900 GList *
901 hildon_touch_selector_get_selected_rows (HildonTouchSelector * selector,
902                                          gint column)
903 {
904   GList *result = NULL;
905   SelectorColumn *current_column = NULL;
906   GtkTreeSelection *selection = NULL;
907
908   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
909   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
910                         NULL);
911
912   current_column = g_slist_nth_data (selector->priv->columns, column);
913   selection = gtk_tree_view_get_selection (current_column->tree_view);
914
915   result = gtk_tree_selection_get_selected_rows (selection, NULL);
916
917
918   return result;
919 }
920
921 GtkTreeModel *
922 hildon_touch_selector_get_model (HildonTouchSelector * selector, gint column)
923 {
924   SelectorColumn *current_column = NULL;
925
926   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
927   g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
928                         NULL);
929
930   current_column = g_slist_nth_data (selector->priv->columns, column);
931
932   return current_column->model;
933 }
934
935 static void
936 _hildon_touch_selector_set_model (HildonTouchSelector * selector,
937                                  gint num_column, GtkTreeModel * model)
938 {
939   SelectorColumn *column = NULL;
940   g_print ("this was actually called\n");
941
942   column =
943     (SelectorColumn *) g_slist_nth_data (selector->priv->columns, num_column);
944
945   column->model = model;
946   gtk_tree_view_set_model (column->tree_view, column->model);
947 }
948
949 void
950 hildon_touch_selector_set_model (HildonTouchSelector * selector,
951                                  gint num_column, GtkTreeModel * model)
952 {
953   g_return_if_fail (HILDON_TOUCH_SELECTOR (selector));
954   g_return_if_fail (num_column < hildon_touch_selector_get_num_columns (selector));
955
956   HILDON_TOUCH_SELECTOR_GET_CLASS (selector)->set_model (selector, num_column, model);
957 }
958 /**
959  * hildon_touch_selector_get_active_text
960  * @selector: the #HildonTouchSelector
961  *
962  * It return a new gchar that represents the current element(s) selected,
963  * using the current print_func.
964  *
965  * Returns: a new allocated gchar*
966  **/
967 gchar *
968 hildon_touch_selector_get_current_text (HildonTouchSelector * selector)
969 {
970   gchar *result = NULL;
971   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
972
973   if (selector->priv->print_func) {
974     result = (*selector->priv->print_func) (selector);
975   } else {
976     result = _default_print_func (selector);
977   }
978
979   return result;
980 }
981
982 static gboolean
983 _hildon_touch_selector_center_on_selected_items (gpointer data)
984 {
985   HildonTouchSelector *selector = NULL;
986   SelectorColumn *column = NULL;
987   GSList *iter_column = NULL;
988   GtkTreeIter iter;
989   GtkTreePath *path;
990   GdkRectangle rect;
991   gint y;
992   gint i;
993   HildonTouchSelectorSelectionMode selection_mode;
994
995   /* ensure to center on the initial values */
996   selector = HILDON_TOUCH_SELECTOR (data);
997
998   selection_mode = hildon_touch_selector_get_column_selection_mode (selector);
999
1000   iter_column = selector->priv->columns;
1001   i = 0;
1002   while (iter_column) {
1003     column = (SelectorColumn *) iter_column->data;
1004
1005     if ((i == 0)
1006         && (selection_mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)) {
1007       break;
1008     }
1009     if (hildon_touch_selector_get_active_iter (selector, i, &iter)) {
1010       path = gtk_tree_model_get_path (column->model, &iter);
1011       gtk_tree_view_get_background_area (GTK_TREE_VIEW
1012                                          (column->tree_view), path, NULL,
1013                                          &rect);
1014
1015       gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW
1016                                                        (column->tree_view), 0,
1017                                                        rect.y, NULL, &y);
1018
1019       hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA
1020                                       (column->panarea), -1, y);
1021
1022       gtk_tree_path_free (path);
1023     }
1024     iter_column = iter_column->next;
1025     i++;
1026   }
1027
1028   return FALSE;
1029 }
1030
1031 static gboolean
1032 _hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
1033 {
1034   HildonTouchSelectorSelectionMode mode = HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
1035   gint n_columns = 0;
1036
1037   n_columns = hildon_touch_selector_get_num_columns (selector);
1038   mode = hildon_touch_selector_get_column_selection_mode (selector);
1039
1040   return ((n_columns > 1) || (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE));
1041 }
1042
1043 /**
1044  * hildon_touch_selector_has_multiple_selection:
1045  * @selector: A #HildonTouchSelector
1046  *
1047  * Determines whether @selector is complex enough to actually require an
1048  * extra selection step than only picking an item. This is normally %TRUE
1049  * if @selector has multiple columns, multiple selection, or when it is a
1050  * more complex widget, like %HildonTouchSelectorEntry.
1051  *
1052  * This information is useful for widgets containing a %HildonTouchSelector,
1053  * like #HildonPickerDialog, that could need a "Done" button, in case that
1054  * its internal #HildonTouchSelector has multiple columns, for instance.
1055  *
1056  * Returns: %TRUE if @selector requires multiple selection steps.
1057  **/
1058 gboolean
1059 hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
1060 {
1061   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
1062
1063   return HILDON_TOUCH_SELECTOR_GET_CLASS (selector)->has_multiple_selection (selector);
1064 }