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