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