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