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
28
 * columns. #HildonTouchSelector does not need to be placed in a
29
 * #HildonPannableArea (in fact, doing so will prevent panning).
30
 *
31
 * Normally, you would use #HildonTouchSelector together with a
32
 * #HildonPickerDialog activated from a button. For the most common
33
 * cases, you should use #HildonPickerButton.
34
 *
35
 * The contents of each #HildonTouchSelector column are stored in a
36
 * #GtkTreeModel. To add a new column to a #HildonTouchSelector, use
37
 * hildon_touch_selector_append_column(). If you want to add a
38
 * text-only column, without special attributes, use
39
 * hildon_touch_selector_append_text_column().
40
 *
41
 * It is highly recommended that you use only one column
42
 * #HildonTouchSelector<!-- -->s.
43
 * If you only need a text only, one column selector, you can create it with
44
 * hildon_touch_selector_new_text() and populate with
45
 * hildon_touch_selector_append_text(), hildon_touch_selector_prepend_text(),
46
 * and hildon_touch_selector_insert_text().
47
 *
48
 * If you need a selector widget that also accepts user inputs, you
49
 * can use #HildonTouchSelectorEntry.
50
 *
51
 * The current selection has a string representation. In the most common cases,
52
 * each column model will contain a text column. You can configure
53
 * which column in particular using the #HildonTouchSelectorColumn property
54
 * #HildonTouchSelectorColumn:text-column
55
 *
56
 * You can get this string representation using
57
 * hildon_touch_selector_get_current_text().
58
 * You can configure how the selection is printed with
59
 * hildon_touch_selector_set_print_func(), that sets the current hildon touch
60
 * selector print function. The widget has a default print function, that
61
 * uses the #HildonTouchSelectorColumn:text-column property on each
62
 * #HildonTouchSelectorColumn to compose the final representation.
63
 *
64
 * If you create the selector using hildon_touch_selector_new_text() you
65
 * don't need to take care of this property, as the model is created internally.
66
 * If you create the selector using hildon_touch_selector_new(), you
67
 * need to specify properly the property for your custom model in order to get a
68
 * non-empty string representation, or define your custom print function.
69
 *
70
 * <example>
71
 * <title>Creating a HildonTouchSelector</title>
72
 * <programlisting>
73
 * void
74
 * selection_changed (HildonTouchSelector *selector,
75
 *                    gint                 column,
76
 *                    gpointer             user_data)
77
 * {
78
 *   gchar *current_selection = NULL;
79
 * <!-- -->
80
 *   current_selection = hildon_touch_selector_get_current_text (selector);
81
 *   g_debug ("Current selection : &percnt;s", current_selection);
82
 * }
83
 * <!-- -->
84
 * static GtkWidget *
85
 * create_customized_selector ()
86
 * {
87
 *   GtkWidget *selector = NULL;
88
 *   GSList *icon_list = NULL;
89
 *   GtkListStore *store_icons = NULL;
90
 *   GSList *item = NULL;
91
 *   GtkCellRenderer *renderer = NULL;
92
 *   HildonTouchSelectorColumn *column = NULL;
93
 * <!-- -->
94
 *   selector = hildon_touch_selector_new ();
95
 * <!-- -->
96
 *   icon_list = gtk_stock_list_ids ();
97
 * <!-- -->
98
 *   store_icons = gtk_list_store_new (1, G_TYPE_STRING);
99
 *   for (item = icon_list; item; item = g_slist_next (item)) {
100
 *     GtkTreeIter iter;
101
 *     gchar *label = item->data;
102
 * <!-- -->
103
 *     gtk_list_store_append (store_icons, &amp;iter);
104
 *     gtk_list_store_set (store_icons, &amp;iter, 0, label, -1);
105
 *     g_free (label);
106
 *   }
107
 *   g_slist_free (icon_list);
108
 * <!-- -->
109
 *   renderer = gtk_cell_renderer_pixbuf_new ();
110
 *   gtk_cell_renderer_set_fixed_size (renderer, -1, 100);
111
 * <!-- -->
112
 *   column = hildon_touch_selector_append_column (HILDON_TOUCH_SELECTOR (selector),
113
 *                                                 GTK_TREE_MODEL (store_icons),
114
 *                                                 renderer, "stock-id", 0, NULL);
115
 * <!-- -->
116
 *   hildon_touch_selector_column_set_text_column (column, 0);
117
 * <!-- -->
118
 *   g_object_unref (store_icons);
119
 * <!-- -->
120
 *   hildon_touch_selector_set_column_selection_mode (HILDON_TOUCH_SELECTOR (selector),
121
 *                                                    HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE);
122
 * <!-- -->
123
 *   g_signal_connect (G_OBJECT (selector), "changed",
124
 *                     G_CALLBACK (selection_changed), NULL);
125
 * <!-- -->
126
 *   return selector;
127
 * }
128
 * <!-- -->
129
 * static GtkWidget *
130
 * create_simple_selector ()
131
 * {
132
 *   GtkWidget *selector = NULL;
133
 *   gint i;
134
 * <!-- -->
135
 *   selector = hildon_touch_selector_new_text ();
136
 *   hildon_touch_selector_set_column_selection_mode (HILDON_TOUCH_SELECTOR (selector),
137
 *                                                    HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE);
138
 * <!-- -->
139
 *   g_signal_connect (G_OBJECT (selector), "changed",
140
 *                     G_CALLBACK (selection_changed), NULL);
141
 * <!-- -->
142
 *   for (i = 1; i <= 10 ; i++) {
143
 *     gchar *label = g_strdup_printf ("Item &amp;percnt;d", i);
144
 * <!-- -->
145
 *     hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
146
 *                                        label);
147
 * <!-- -->
148
 *     g_free (label);
149
 *   }
150
 * <!-- -->
151
 *   return selector;
152
 * }
153
 * </programlisting>
154
 * </example>
155
 */
156
157
/**
158
 * SECTION:hildon-touch-selector-column
159
 * @short_description: A visible column in a #HildonTouchSelector
160
 * @see_also: #HildonTouchSelector
161
 *
162
 * A #HildonTouchSelectorColumn is a column in a
163
 * #HildonTouchSelector. This class implements the #GtkCellLayout interface, allowing
164
 * a flexible management of the cellrenderers in each #HildonTouchSelector column.
165
 */
166
167
#undef HILDON_DISABLE_DEPRECATED
168
169
#ifdef HAVE_CONFIG_H
170
#include <config.h>
171
#endif
172
173
#include <string.h>
174
#include <stdlib.h>
175
#include <glib.h>
176
177
#include "hildon-gtk.h"
178
179
#include "hildon-pannable-area.h"
180
#include "hildon-touch-selector.h"
181
#include "hildon-touch-selector-private.h"
182
#include "hildon-live-search.h"
183
184
#define HILDON_TOUCH_SELECTOR_GET_PRIVATE(obj)                          \
185
  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_TOUCH_SELECTOR, HildonTouchSelectorPrivate))
186
187
G_DEFINE_TYPE (HildonTouchSelector, hildon_touch_selector, GTK_TYPE_VBOX)
188
189
/*
190
 * IMPLEMENTATION NOTES:
191
 * Struct to maintain the data of each column. The columns are the elements
192
 * of the widget that belongs properly to the selection behaviour. Although
193
 * internally the columns are arranged in a private #GtkHBox, as the selector
194
 * itself is a #GtkVBox, you can add more widgets, like buttons etc., so
195
 * you finally could have a widget with more elements that the columns, but
196
 * this doesn't belongs to the selection logic
197
 */
198
struct _HildonTouchSelectorColumnPrivate
199
{
200
  HildonTouchSelector *parent;    /* the selector that contains this column */
201
  GtkTreeModel *model;
202
  gint text_column;
203
  GtkTreeView *tree_view;
204
  gulong realize_handler;
205
  GtkTreePath *initial_path;
206
  GtkTreeModel *filter;
207
  GtkWidget *livesearch;
208
209
  GtkWidget *panarea;           /* the pannable widget */
210
  GtkWidget *vbox;
211
  GtkTreeRowReference *last_activated;
212
};
213
214
struct _HildonTouchSelectorPrivate
215
{
216
  GSList *columns;              /* the selection columns */
217
  GtkWidget *hbox;              /* the container for the selector's columns */
218
  gboolean initial_scroll;      /* whether initial fancy scrolling to selection */
219
  gboolean has_live_search;
220
  GSList *norm_tokens;
221
222
  gboolean changed_blocked;
223
224
  HildonTouchSelectorPrintFunc print_func;
225
  gpointer print_user_data;
226
  GDestroyNotify print_destroy_func;
227
228
  HildonUIMode hildon_ui_mode;
229
};
230
231
enum
232
{
233
  PROP_HAS_MULTIPLE_SELECTION = 1,
234
  PROP_INITIAL_SCROLL,
235
  PROP_HILDON_UI_MODE,
236
  PROP_LIVE_SEARCH
237
};
238
239
enum
240
{
241
  CHANGED,
242
  COLUMNS_CHANGED,
243
  LAST_SIGNAL
244
};
245
246
static gint hildon_touch_selector_signals[LAST_SIGNAL] = { 0 };
247
248
static void
249
hildon_touch_selector_dispose                   (GObject * object);
250
251
static void
252
hildon_touch_selector_get_property              (GObject * object,
253
                                                 guint prop_id,
254
                                                 GValue * value,
255
                                                 GParamSpec * pspec);
256
static void
257
hildon_touch_selector_set_property              (GObject *object,
258
                                                 guint prop_id,
259
                                                 const GValue *value,
260
                                                 GParamSpec *pspec);
261
/* gtkwidget */
262
263
/* gtkcontainer */
264
static void hildon_touch_selector_remove        (GtkContainer * container,
265
                                                 GtkWidget * widget);
266
/* private functions */
267
static void _row_tapped_cb                      (GtkTreeView * tree_view,
268
                                                 GtkTreePath * path,
269
                                                 gpointer user_data);
270
static void
271
hildon_touch_selector_row_activated_cb          (GtkTreeView       *tree_view,
272
                                                 GtkTreePath       *path,
273
                                                 GtkTreeViewColumn *column,
274
                                                 gpointer           user_data);
275
276
static gchar *_default_print_func               (HildonTouchSelector * selector,
277
                                                 gpointer user_data);
278
279
static HildonTouchSelectorColumn *_create_new_column (HildonTouchSelector * selector,
280
                                                 GtkTreeModel * model,
281
                                                 gboolean *emit_changed,
282
                                                 GtkCellRenderer * renderer,
283
                                                 va_list args);
284
static gboolean
285
on_realize_cb                                  (GtkWidget *widget,
286
                                                gpointer data);
287
static void
288
on_row_changed                                 (GtkTreeModel *model,
289
                                                GtkTreePath *path,
290
                                                GtkTreeIter *iter,
291
                                                gpointer userdata);
292
static void
293
on_row_deleted                                 (GtkTreeModel *model,
294
                                                GtkTreePath *path,
295
                                                gpointer userdata);
296
297
static void
298
hildon_touch_selector_scroll_to (HildonTouchSelectorColumn *column,
299
                                 GtkTreeView *tv,
300
                                 GtkTreePath *path);
301
static gboolean
302
_hildon_touch_selector_center_on_selected_items (HildonTouchSelector *selector,
303
                                                 HildonTouchSelectorColumn *column);
304
static void
305
_hildon_touch_selector_set_model                (HildonTouchSelector * selector,
306
                                                 gint num_column,
307
                                                 GtkTreeModel * model);
308
static gboolean
309
_hildon_touch_selector_has_multiple_selection   (HildonTouchSelector * selector);
310
311
static void
312
hildon_touch_selector_emit_value_changed        (HildonTouchSelector *selector,
313
                                                 gint column);
314
315
/* GtkCellLayout implementation (HildonTouchSelectorColumn)*/
316
static void hildon_touch_selector_column_cell_layout_init         (GtkCellLayoutIface      *iface);
317
318
static void hildon_touch_selector_column_cell_layout_pack_start   (GtkCellLayout         *cell_layout,
319
                                                                   GtkCellRenderer       *cell,
320
                                                                   gboolean               expand);
321
static void hildon_touch_selector_column_cell_layout_pack_end     (GtkCellLayout         *cell_layout,
322
                                                                   GtkCellRenderer       *cell,
323
                                                                   gboolean               expand);
324
static void hildon_touch_selector_column_cell_layout_clear        (GtkCellLayout         *cell_layout);
325
static void hildon_touch_selector_column_cell_layout_add_attribute(GtkCellLayout         *cell_layout,
326
                                                                   GtkCellRenderer       *cell,
327
                                                                   const gchar           *attribute,
328
                                                                   gint                   column);
329
static void hildon_touch_selector_column_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
330
                                                                         GtkCellRenderer       *cell,
331
                                                                         GtkCellLayoutDataFunc  func,
332
                                                                         gpointer               func_data,
333
                                                                         GDestroyNotify         destroy);
334
static void hildon_touch_selector_column_cell_layout_clear_attributes   (GtkCellLayout         *cell_layout,
335
                                                                         GtkCellRenderer       *cell);
336
static void hildon_touch_selector_column_cell_layout_reorder       (GtkCellLayout         *cell_layout,
337
                                                                    GtkCellRenderer       *cell,
338
                                                                    gint                   position);
339
static GList *hildon_touch_selector_column_cell_layout_get_cells   (GtkCellLayout         *cell_layout);
340
341
static void
342
hildon_touch_selector_check_ui_mode_coherence   (HildonTouchSelector *selector);
343
344
static void
345
hildon_touch_selector_class_init (HildonTouchSelectorClass * class)
346
{
347
  GObjectClass *gobject_class;
348
  GtkObjectClass *object_class;
349
  GtkContainerClass *container_class;
350
351
  gobject_class = G_OBJECT_CLASS (class);
352
  object_class = GTK_OBJECT_CLASS (class);
353
  container_class = GTK_CONTAINER_CLASS (class);
354
355
  /* GObject */
356
  gobject_class->dispose = hildon_touch_selector_dispose;
357
  gobject_class->get_property = hildon_touch_selector_get_property;
358
  gobject_class->set_property = hildon_touch_selector_set_property;
359
360
  /* GtkWidget */
361
362
  /* GtkContainer */
363
  container_class->remove = hildon_touch_selector_remove;
364
365
  /* HildonTouchSelector */
366
  class->changed = NULL;
367
  class->set_model = _hildon_touch_selector_set_model;
368
369
  class->has_multiple_selection = _hildon_touch_selector_has_multiple_selection;
370
371
  /* signals */
372
  /**
373
   * HildonTouchSelector::changed:
374
   * @widget: the object which received the signal
375
   * @column: the number of the column that has changed
376
   *
377
   * The "changed" signal is emitted when the active item on any column is changed.
378
   * This can be due to the user selecting a different item from the list, or
379
   * due to a call to hildon_touch_selector_select_iter() on one of the columns.
380
   *
381
   * Since: 2.2
382
   */
383
  hildon_touch_selector_signals[CHANGED] =
384
    g_signal_new ("changed",
385
                  G_OBJECT_CLASS_TYPE (class),
386
                  G_SIGNAL_RUN_LAST,
387
                  G_STRUCT_OFFSET (HildonTouchSelectorClass, changed),
388
                  NULL, NULL,
389
                  g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
390
391
  /**
392
   * HildonTouchSelector::columns-changed:
393
   * @selector: the object which received the signal
394
   *
395
   * The "columns-changed" signal is emitted when the number
396
   * of columns in the #HildonTouchSelector change.
397
   *
398
   * Since: 2.2
399
   */
400
  hildon_touch_selector_signals[COLUMNS_CHANGED] =
401
    g_signal_new ("columns-changed",
402
                  G_OBJECT_CLASS_TYPE (class),
403
                  G_SIGNAL_RUN_LAST, 0,
404
                  NULL, NULL,
405
                  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
406
407
  /* properties */
408
409
  g_object_class_install_property (gobject_class, PROP_HAS_MULTIPLE_SELECTION,
410
                                   g_param_spec_boolean ("has-multiple-selection",
411
                                                         "has multiple selection",
412
                                                         "Whether the widget has multiple "
413
                                                         "selection (like multiple columns, "
414
                                                         "multiselection mode, or multiple "
415
                                                         "internal widgets) and therefore "
416
                                                         "it may need a confirmation button, "
417
                                                         "for instance.",
418
                                                         FALSE,
419
                                                         G_PARAM_READABLE));
420
421
  g_object_class_install_property (G_OBJECT_CLASS (gobject_class),
422
                                   PROP_INITIAL_SCROLL,
423
                                   g_param_spec_boolean ("initial-scroll",
424
                                                         "Initial scroll",
425
                                                         "Whether to scroll to the"
426
                                                         "current selection when"
427
                                                         "the selector is first"
428
                                                         "shown",
429
                                                         TRUE,
430
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
431
432
    /**
433
     * HildonTouchSelector:hildon-ui-mode:
434
     *
435
     * Specifies which UI mode to use in the internal treeviews.  A setting
436
     * of %HILDON_UI_MODE_NORMAL will cause these tree view to disable selections
437
     * and emit row-activated as soon as a row is pressed (unless it is pressed
438
     * to drag the pannable area where the treeview is). You can use the
439
     * method hildon_touch_selector_get_last_activated_row() to get it. When
440
     * %HILDON_UI_MODE_EDIT is set, selections can be made according to the
441
     * setting of the mode on GtkTreeSelection.
442
     *
443
     * Toggling this property will cause the tree view to select an
444
     * appropriate selection mode if not already done.
445
     *
446
     * Since: Hildon 2.2
447
     */
448
  g_object_class_install_property (gobject_class,
449
                                   PROP_HILDON_UI_MODE,
450
                                   g_param_spec_enum ("hildon-ui-mode",
451
                                                      "Hildon UI Mode",
452
                                                      "The Hildon UI mode according "
453
                                                      "to which the touch selector "
454
                                                      "should behave",
455
                                                      HILDON_TYPE_UI_MODE,
456
                                                      HILDON_UI_MODE_EDIT,
457
                                                      G_PARAM_READWRITE));
458
459
  g_object_class_install_property (G_OBJECT_CLASS (gobject_class),
460
                                   PROP_LIVE_SEARCH,
461
                                   g_param_spec_boolean ("live-search",
462
                                                         "Live search",
463
                                                         "Whether the widget should have built-in"
464
                                                         "live search capabilities",
465
                                                         TRUE,
466
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
467
468
  /* style properties */
469
  /* We need to ensure fremantle mode for the treeview in order to work
470
     properly. This is not about the appearance, this is about behaviour */
471
  gtk_rc_parse_string ("style \"fremantle-htst\" {\n"
472
                       "  GtkWidget::hildon-mode = 1\n"
473
                       "} widget \"*.fremantle-htst\" style \"fremantle-htst\""
474
                       "widget_class \"*<HildonPannableArea>.GtkTreeView\" style :highest \"fremantle-htst\"");
475
476
  g_type_class_add_private (object_class, sizeof (HildonTouchSelectorPrivate));
477
}
478
479
static void
480
hildon_touch_selector_get_property (GObject * object,
481
                                    guint prop_id,
482
                                    GValue * value, GParamSpec * pspec)
483
{
484
  HildonTouchSelectorPrivate *priv = HILDON_TOUCH_SELECTOR (object)->priv;
485
486
  switch (prop_id) {
487
  case PROP_HAS_MULTIPLE_SELECTION:
488
    g_value_set_boolean (value,
489
                         hildon_touch_selector_has_multiple_selection (HILDON_TOUCH_SELECTOR (object)));
490
    break;
491
  case PROP_INITIAL_SCROLL:
492
    g_value_set_boolean (value, priv->initial_scroll);
493
    break;
494
  case PROP_HILDON_UI_MODE:
495
    g_value_set_enum (value, priv->hildon_ui_mode);
496
    break;
497
  case PROP_LIVE_SEARCH:
498
    g_value_set_boolean (value,
499
                         hildon_touch_selector_get_live_search (HILDON_TOUCH_SELECTOR (object)));
500
    break;
501
  default:
502
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
503
    break;
504
  }
505
}
506
507
static void
508
hildon_touch_selector_set_property (GObject *object, guint prop_id,
509
                                    const GValue *value, GParamSpec *pspec)
510
{
511
  HildonTouchSelectorPrivate *priv = HILDON_TOUCH_SELECTOR (object)->priv;
512
513
  switch (prop_id) {
514
  case PROP_INITIAL_SCROLL:
515
    priv->initial_scroll = g_value_get_boolean (value);
516
    break;
517
  case PROP_HILDON_UI_MODE:
518
    hildon_touch_selector_set_hildon_ui_mode (HILDON_TOUCH_SELECTOR (object),
519
                                              g_value_get_enum (value));
520
    break;
521
  case PROP_LIVE_SEARCH:
522
    hildon_touch_selector_set_live_search (HILDON_TOUCH_SELECTOR (object),
523
                                           g_value_get_boolean (value));
524
    break;
525
  default:
526
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
527
    break;
528
  }
529
}
530
531
532
static void
533
hildon_touch_selector_init (HildonTouchSelector * selector)
534
{
535
  selector->priv = HILDON_TOUCH_SELECTOR_GET_PRIVATE (selector);
536
537
  GTK_WIDGET_SET_FLAGS (GTK_WIDGET (selector), GTK_NO_WINDOW);
538
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (selector), FALSE);
539
540
  selector->priv->columns = NULL;
541
542
  selector->priv->norm_tokens = NULL;
543
  selector->priv->print_func = NULL;
544
  selector->priv->print_user_data = NULL;
545
  selector->priv->print_destroy_func = NULL;
546
  selector->priv->initial_scroll = TRUE;
547
  selector->priv->hbox = gtk_hbox_new (FALSE, 0);
548
549
  selector->priv->changed_blocked = FALSE;
550
551
  selector->priv->hildon_ui_mode = HILDON_UI_MODE_EDIT;
552
553
  gtk_box_pack_end (GTK_BOX (selector), selector->priv->hbox,
554
                    TRUE, TRUE, 0);
555
  gtk_widget_show (selector->priv->hbox);
556
}
557
558
static void
559
hildon_touch_selector_dispose (GObject * object)
560
{
561
  GObjectClass *gobject_class;
562
  HildonTouchSelector *selector = HILDON_TOUCH_SELECTOR (object);
563
564
  hildon_touch_selector_set_print_func_full (selector,
565
                                             NULL, NULL, NULL);
566
567
  if (selector->priv->norm_tokens != NULL) {
568
      g_slist_foreach (selector->priv->norm_tokens, (GFunc) g_free, NULL);
569
      g_slist_free (selector->priv->norm_tokens);
570
      selector->priv->norm_tokens = NULL;
571
  }
572
573
  gobject_class = G_OBJECT_CLASS (hildon_touch_selector_parent_class);
574
575
  if (gobject_class->dispose)
576
    (* gobject_class->dispose) (object);
577
}
578
579
static void
580
clean_column                                    (HildonTouchSelectorColumn *col,
581
                                                 HildonTouchSelector *selector)
582
{
583
  g_signal_handlers_disconnect_by_func (col->priv->model,
584
                                        on_row_changed, selector);
585
  g_signal_handlers_disconnect_by_func (col->priv->model,
586
                                        on_row_deleted, selector);
587
588
  if (col->priv->last_activated != NULL) {
589
    gtk_tree_row_reference_free (col->priv->last_activated);
590
    col->priv->last_activated = NULL;
591
  }
592
}
593
594
/*
595
 * IMPLEMENTATION NOTES:
596
 * Some people sent questions regarding the fact that the dispose/finalize functions
597
 * doesn't clean the internal widgets that could lead to leak memory, so we will
598
 * clarify this topic.
599
 *
600
 * This is not required as #HildonTouchSelector extends #GtkContainer. When the
601
 * widget is freed, the #GtkContainer freeing memory functions are called. This
602
 * process includes remove each widget individually, so all the widgets are
603
 * properly freed.
604
 *
605
 * In the same way, this widget redefines gtk_container->remove function, in
606
 * order to free the column related information if it is required.
607
 *
608
 * Please take a look to hildon_touch_selector_remove for more information.
609
 */
610
611
/*------------------------------ GtkContainer ------------------------------ */
612
613
/*
614
 * Required in order to free the column at the columns list
615
 */
616
static void
617
hildon_touch_selector_remove (GtkContainer * container, GtkWidget * widget)
618
{
619
  HildonTouchSelector *selector = NULL;
620
621
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (container));
622
  selector = HILDON_TOUCH_SELECTOR (container);
623
624
  /* Remove the extra data related to the columns, if required. */
625
  if (widget == selector->priv->hbox) {
626
    g_slist_foreach (selector->priv->columns, (GFunc) clean_column, selector);
627
    g_slist_foreach (selector->priv->columns, (GFunc) g_object_unref, NULL);
628
629
    g_slist_free (selector->priv->columns);
630
631
    selector->priv->columns = NULL;
632
  }
633
634
  /* Now remove the widget itself from the container */
635
  GTK_CONTAINER_CLASS (hildon_touch_selector_parent_class)->remove (container, widget);
636
}
637
638
/* ------------------------------ PRIVATE METHODS ---------------------------- */
639
void
640
hildon_touch_selector_block_changed             (HildonTouchSelector *selector)
641
{
642
  selector->priv->changed_blocked = TRUE;
643
}
644
645
void
646
hildon_touch_selector_unblock_changed           (HildonTouchSelector *selector)
647
{
648
  selector->priv->changed_blocked = FALSE;
649
}
650
651
static void
652
hildon_touch_selector_emit_value_changed        (HildonTouchSelector *selector,
653
                                                 gint column)
654
{
655
  /* FIXME: it could be good to emit too the GtkTreePath of the element
656
     selected, as now it is required to connect to the signal and then ask
657
     for the element selected. We can't do this API change, in order to avoid
658
     and ABI break */
659
  if (!selector->priv->changed_blocked) {
660
    if (hildon_touch_selector_get_column_selection_mode (selector) == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE &&
661
        selector->priv->columns) {
662
      HildonTouchSelectorColumn *col;
663
      col = (HildonTouchSelectorColumn *) selector->priv->columns->data;
664
      if (col->priv->livesearch) {
665
        hildon_live_search_clean_selection_map (HILDON_LIVE_SEARCH (col->priv->livesearch));
666
      }
667
    }
668
    g_signal_emit (selector, hildon_touch_selector_signals[CHANGED], 0, column);
669
  }
670
}
671
672
static void
673
hildon_touch_selector_check_ui_mode_coherence   (HildonTouchSelector *selector)
674
{
675
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
676
677
  if (hildon_touch_selector_get_num_columns (selector) > 1) {
678
    hildon_touch_selector_set_hildon_ui_mode (selector, HILDON_UI_MODE_EDIT);
679
  }
680
}
681
682
/**
683
 * default_print_func:
684
 * @selector: a #HildonTouchSelector
685
 *
686
 * Default print function
687
 *
688
 * Returns: a new string that represents the selected items
689
 *
690
 * Since: 2.2
691
 **/
692
static gchar *
693
_default_print_func (HildonTouchSelector * selector, gpointer user_data)
694
{
695
  gchar *result = NULL;
696
  gchar *aux = NULL;
697
  gint num_columns = 0;
698
  GtkTreeIter iter;
699
  GtkTreeModel *model = NULL;
700
  gchar *current_string = NULL;
701
  gint i;
702
  HildonTouchSelectorSelectionMode mode;
703
  GList *item = NULL;
704
  GtkTreePath *current_path = NULL;
705
  GList *selected_rows = NULL;
706
  gint initial_value = 0;
707
  gint text_column = -1;
708
  HildonTouchSelectorColumn *column = NULL;
709
710
  num_columns = hildon_touch_selector_get_num_columns (selector);
711
712
  mode = hildon_touch_selector_get_column_selection_mode (selector);
713
714
  if ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
715
      && (num_columns > 0)) {
716
    /* In this case we get the first column first */
717
    selected_rows = hildon_touch_selector_get_selected_rows (selector, 0);
718
    model = hildon_touch_selector_get_model (selector, 0);
719
    column = hildon_touch_selector_get_column (selector, 0);
720
    text_column = hildon_touch_selector_column_get_text_column (column);
721
722
    result = g_strdup_printf ("(");
723
    i = 0;
724
    for (item = selected_rows; item; item = g_list_next (item)) {
725
      current_path = item->data;
726
      gtk_tree_model_get_iter (model, &iter, current_path);
727
728
      if (text_column != -1) {
729
        gtk_tree_model_get (model, &iter, text_column, &current_string, -1);
730
      }
731
732
      if (i < g_list_length (selected_rows) - 1) {
733
        aux = g_strconcat (result, current_string, ",", NULL);
734
        g_free (result);
735
        result = aux;
736
      } else {
737
        aux = g_strconcat (result, current_string, NULL);
738
        g_free (result);
739
        result = aux;
740
      }
741
742
      if (current_string) {
743
        g_free (current_string);
744
        current_string = NULL;
745
      }
746
747
      i++;
748
    }
749
750
    aux = g_strconcat (result, ")", NULL);
751
    g_free (result);
752
    result = aux;
753
754
    g_list_foreach (selected_rows, (GFunc) (gtk_tree_path_free), NULL);
755
    g_list_free (selected_rows);
756
    initial_value = 1;
757
  } else {
758
    initial_value = 0;
759
  }
760
761
  for (i = initial_value; i < num_columns; i++) {
762
    model = hildon_touch_selector_get_model (selector, i);
763
    column = hildon_touch_selector_get_column (selector, i);
764
    text_column = hildon_touch_selector_column_get_text_column (column);
765
766
    if (hildon_touch_selector_get_selected (selector, i, &iter)) {
767
      if (text_column == -1 ) {
768
        g_warning ("Trying to use the default print function in HildonTouchSelector, but "
769
                   "\"text-column\" property is not set for HildonTouchSelectorColumn %p.", column);
770
        current_string = NULL;
771
      } else {
772
        gtk_tree_model_get (model, &iter, text_column, &current_string, -1);
773
      }
774
775
      if (i == 0) {
776
        result = current_string;
777
      } else {
778
        aux = g_strconcat (result, ":", current_string, NULL);
779
        g_free (result);
780
        g_free (current_string);
781
        current_string = NULL;
782
        result = aux;
783
      }
784
    }
785
  }
786
787
  return result;
788
}
789
790
static void
791
hildon_touch_selector_row_activated_cb          (GtkTreeView       *tree_view,
792
                                                 GtkTreePath       *path,
793
                                                 GtkTreeViewColumn *column,
794
                                                 gpointer           user_data)
795
{
796
  HildonTouchSelectorColumn *selector_column = NULL;
797
  GtkTreePath *child_path;
798
799
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (user_data));
800
  selector_column = HILDON_TOUCH_SELECTOR_COLUMN (user_data);
801
802
  if (selector_column->priv->last_activated != NULL) {
803
    gtk_tree_row_reference_free (selector_column->priv->last_activated);
804
  }
805
806
  child_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (selector_column->priv->filter),
807
                                                                 path);
808
  selector_column->priv->last_activated = gtk_tree_row_reference_new (selector_column->priv->model,
809
                                                                      child_path);
810
  gtk_tree_path_free (child_path);
811
}
812
813
static void
814
_row_tapped_cb (GtkTreeView * tree_view, GtkTreePath * path, gpointer user_data)
815
{
816
  HildonTouchSelector *selector = NULL;
817
  HildonTouchSelectorColumn *column = NULL;
818
  gint num_column = -1;
819
820
  column = HILDON_TOUCH_SELECTOR_COLUMN (user_data);
821
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (column->priv->parent));
822
823
  selector = column->priv->parent;
824
825
  num_column = g_slist_index (selector->priv->columns, column);
826
827
  hildon_touch_selector_emit_value_changed (selector, num_column);
828
}
829
830
831
static HildonTouchSelectorColumn *
832
_create_new_column (HildonTouchSelector * selector,
833
                    GtkTreeModel * model,
834
                    gboolean *emit_changed,
835
                    GtkCellRenderer * renderer, va_list args)
836
{
837
  HildonTouchSelectorColumn *new_column = NULL;
838
  GtkTreeViewColumn *tree_column = NULL;
839
  GtkTreeView *tv = NULL;
840
  GtkTreeModel *filter;
841
  GtkWidget *panarea = NULL;
842
  GtkTreeSelection *selection = NULL;
843
  GtkTreeIter iter;
844
  gchar *attribute;
845
  gint value;
846
847
  tree_column = gtk_tree_view_column_new ();
848
849
  if (renderer != NULL) {
850
    gtk_tree_view_column_pack_start (tree_column, renderer, TRUE);
851
852
    attribute = va_arg (args, gchar *);
853
    while (attribute != NULL) {
854
      value = va_arg (args, gint);
855
      gtk_tree_view_column_add_attribute (tree_column, renderer, attribute,
856
                                          value);
857
      attribute = va_arg (args, gchar *);
858
    }
859
  }
860
861
#ifdef MAEMO_GTK
862
  tv = GTK_TREE_VIEW (hildon_gtk_tree_view_new (selector->priv->hildon_ui_mode));
863
#else
864
  tv = GTK_TREE_VIEW (gtk_tree_view_new ());
865
#endif /* MAEMO_GTK */
866
867
  gtk_tree_view_set_enable_search (tv, FALSE);
868
  if (!selector->priv->has_live_search) {
869
    GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (tv), GTK_CAN_FOCUS);
870
  }
871
872
  filter = gtk_tree_model_filter_new (model, NULL);
873
  gtk_tree_view_set_model (tv, filter);
874
  g_signal_connect (model, "row-changed",
875
                    G_CALLBACK (on_row_changed), selector);
876
  g_signal_connect_after (model, "row-deleted",
877
                          G_CALLBACK (on_row_deleted), selector);
878
  gtk_tree_view_set_rules_hint (tv, TRUE);
879
880
  gtk_tree_view_append_column (GTK_TREE_VIEW (tv), tree_column);
881
882
  new_column = g_object_new (HILDON_TYPE_TOUCH_SELECTOR_COLUMN, NULL);
883
  new_column->priv->parent = selector;
884
885
  panarea = hildon_pannable_area_new ();
886
887
  gtk_container_add (GTK_CONTAINER (panarea), GTK_WIDGET (tv));
888
889
  new_column->priv->model = g_object_ref (model);
890
  new_column->priv->tree_view = tv;
891
  new_column->priv->panarea = panarea;
892
  new_column->priv->filter = filter;
893
  new_column->priv->livesearch = NULL;
894
895
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
896
  gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
897
898
  /* select the first item */
899
  *emit_changed = FALSE;
900
  if ((gtk_tree_model_get_iter_first (filter, &iter))&&
901
      (selector->priv->hildon_ui_mode == HILDON_UI_MODE_EDIT)) {
902
    gtk_tree_selection_select_iter (selection, &iter);
903
    *emit_changed = TRUE;
904
  }
905
906
  /* connect to the hildon-row-tapped signal connection */
907
  g_signal_connect (G_OBJECT (tv), "hildon-row-tapped",
908
                    G_CALLBACK (_row_tapped_cb), new_column);
909
910
  g_signal_connect (G_OBJECT (tv), "row-activated",
911
                    G_CALLBACK (hildon_touch_selector_row_activated_cb), new_column);
912
913
  return new_column;
914
}
915
916
917
/* ------------------------ HildonTouchSelectorColumn implementation ---------------------- */
918
G_DEFINE_TYPE_WITH_CODE (HildonTouchSelectorColumn, hildon_touch_selector_column, G_TYPE_OBJECT,
919
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
920
                                                hildon_touch_selector_column_cell_layout_init))
921
922
enum
923
{
924
  PROP_TEXT_COLUMN = 1
925
};
926
927
static void
928
hildon_touch_selector_column_class_init (HildonTouchSelectorColumnClass *klass);
929
930
static void
931
hildon_touch_selector_column_get_property (GObject *object, guint property_id,
932
                                           GValue *value, GParamSpec *pspec);
933
934
static void
935
hildon_touch_selector_column_set_property  (GObject *object, guint property_id,
936
                                            const GValue *value, GParamSpec *pspec);
937
938
static void
939
hildon_touch_selector_column_dispose       (GObject *object);
940
941
static void
942
hildon_touch_selector_column_finalize      (GObject *object);
943
944
static void
945
hildon_touch_selector_column_class_init (HildonTouchSelectorColumnClass *klass)
946
{
947
  GObjectClass *gobject_class = NULL;
948
949
  gobject_class = G_OBJECT_CLASS (klass);
950
951
  g_type_class_add_private (gobject_class, sizeof (HildonTouchSelectorColumnPrivate));
952
953
  /* GObject */
954
  gobject_class->get_property = hildon_touch_selector_column_get_property;
955
  gobject_class->set_property = hildon_touch_selector_column_set_property;
956
  gobject_class->dispose      = hildon_touch_selector_column_dispose;
957
  gobject_class->finalize     = hildon_touch_selector_column_finalize;
958
959
  /**
960
   * HildonTouchSelectorColumn:text-column:
961
   *
962
   * A column in the data source model to get the strings from.
963
   *
964
   * Since: maemo 2.2
965
   **/
966
  g_object_class_install_property (G_OBJECT_CLASS(klass),
967
                                   PROP_TEXT_COLUMN,
968
                                   g_param_spec_int ("text-column",
969
                                                     "Text Column",
970
                                                     "A column in the data source model to get the strings from.",
971
                                                     -1,
972
                                                     G_MAXINT,
973
                                                     -1,
974
                                                     G_PARAM_READWRITE));
975
}
976
977
static void
978
hildon_touch_selector_column_init (HildonTouchSelectorColumn *column)
979
{
980
  column->priv = G_TYPE_INSTANCE_GET_PRIVATE (column, HILDON_TYPE_TOUCH_SELECTOR_COLUMN,
981
                                              HildonTouchSelectorColumnPrivate);
982
  column->priv->text_column = -1;
983
  column->priv->last_activated = NULL;
984
  column->priv->realize_handler = 0;
985
  column->priv->initial_path = NULL;
986
}
987
988
/**
989
 * stripped_char:
990
 *
991
 * Returns a stripped version of @ch, removing any case, accentuation
992
 * mark, or any special mark on it.
993
 **/
994
static gunichar
995
stripped_char (gunichar ch)
996
{
997
  gunichar *decomp, retval;
998
  GUnicodeType utype;
999
  gsize dlen;
1000
1001
  utype = g_unichar_type (ch);
1002
1003
  switch (utype) {
1004
  case G_UNICODE_CONTROL:
1005
  case G_UNICODE_FORMAT:
1006
  case G_UNICODE_UNASSIGNED:
1007
  case G_UNICODE_COMBINING_MARK:
1008
    /* Ignore those */
1009
    return 0;
1010
    break;
1011
  default:
1012
    /* Convert to lowercase, fall through */
1013
    ch = g_unichar_tolower (ch);
1014
  case G_UNICODE_LOWERCASE_LETTER:
1015
    if ((decomp = g_unicode_canonical_decomposition (ch, &dlen))) {
1016
      retval = decomp[0];
1017
      g_free (decomp);
1018
      return retval;
1019
    }
1020
    break;
1021
  }
1022
1023
  return 0;
1024
}
1025
1026
static gchar *
1027
e_util_unicode_get_utf8 (const gchar *text, gunichar *out)
1028
{
1029
  *out = g_utf8_get_char (text);
1030
  return (*out == (gunichar)-1) ? NULL : g_utf8_next_char (text);
1031
}
1032
1033
/**
1034
 * get_next:
1035
 * @p: a pointer to the string to search.
1036
 * @out: a place to store the next valid
1037
 * @separators: whether to search only for alphanumeric strings
1038
 * and skip any word separator.
1039
 *
1040
 * Gets the next character that is valid in our search scope, and
1041
 * store it into @out. The next char, after @out is returned.
1042
 *
1043
 * Returns: the next point in the string @p where to continue the
1044
 * string iteration.
1045
 **/
1046
static const gchar *
1047
get_next (const gchar *p, gunichar *out, gboolean separators)
1048
{
1049
  const gchar *q;
1050
  gunichar utf8;
1051
1052
  if (separators) {
1053
    do {
1054
       q = p;
1055
       p = e_util_unicode_get_utf8 (q, &utf8);
1056
       *out = stripped_char (utf8);
1057
    } while (p && *out && !g_unichar_isalnum (*out));
1058
  } else {
1059
    p = e_util_unicode_get_utf8 (p, &utf8);
1060
    *out = stripped_char (utf8);
1061
  }
1062
1063
  return p;
1064
}
1065
1066
/**
1067
 * e_util_utf8_strstrcasedecomp_needle_stripped:
1068
 * @haystack: a haystack where to search
1069
 * @nuni: a needle to search for, already stripped with strip_string()
1070
 *
1071
 * Heavily modified version of e_util_utf8_strstrcasedecomp(). As its
1072
 * original version, it finds the first occurrence of @nuni in
1073
 * @haystack.  However, instead of stripping @nuni, it expect it to be
1074
 * already stripped.
1075
 *
1076
 * This is done for performance reasons, since this search is done
1077
 * several times for the same string @nuni, it is undesired to strip
1078
 * it more than once.
1079
 *
1080
 * Also, the search is done as a prefix search, starting in the first
1081
 * alphanumeric character after any non-alphanumeric one. Searching
1082
 * for "aba" in "Abasto" will match, searching in "Moraba" will not,
1083
 * and searching in "A tool (abacus)" will do.
1084
 *
1085
 * Returns: the first instance of @nuni in @haystack
1086
 **/
1087
static const gchar *
1088
e_util_utf8_strstrcasedecomp_needle_stripped (const gchar *haystack, const gunichar *nuni)
1089
{
1090
  gunichar unival;
1091
  gint nlen = 0;
1092
  const gchar *o, *p;
1093
  gunichar sc;
1094
1095
  if (haystack == NULL) return NULL;
1096
  if (nuni == NULL) return NULL;
1097
  if (strlen (haystack) == 0) return NULL;
1098
  while (*(nuni + nlen) != 0) nlen++;
1099
1100
  if (nlen < 1) return haystack;
1101
1102
  o = haystack;
1103
1104
  for (p = get_next (o, &sc, g_unichar_isalnum (nuni[0]));
1105
       p && sc;
1106
       p = get_next (p, &sc, g_unichar_isalnum (nuni[0]))) {
1107
    if (sc) {
1108
      /* We have valid stripped gchar */
1109
      if (sc == nuni[0]) {
1110
        const gchar *q = p;
1111
        gint npos = 1;
1112
        while (npos < nlen) {
1113
          q = e_util_unicode_get_utf8 (q, &unival);
1114
          if (!q || !unival) return NULL;
1115
          sc = stripped_char (unival);
1116
          if ((!sc) || (sc != nuni[npos])) break;
1117
          npos++;
1118
        }
1119
        if (npos == nlen) {
1120
          return o;
1121
        }
1122
      }
1123
    }
1124
    while (p) {
1125
      sc = g_utf8_get_char (p);
1126
      if (!g_unichar_isalnum (sc))
1127
        break;
1128
      p = g_utf8_next_char (p);
1129
    }
1130
1131
    o = p;
1132
  }
1133
1134
  return NULL;
1135
}
1136
1137
/**
1138
 * strip_string:
1139
 * @string: a string to be stripped off.
1140
 *
1141
 * Strips all capitalization and accentuation marks from a string.
1142
 * The returned unicode string is 0 terminated.
1143
 *
1144
 * Returns: an unicode, lowercase, and without accentuation marks
1145
 * version of @string, or %NULL if @string is an empty string.
1146
 **/
1147
static gunichar *
1148
strip_string (const gchar *string)
1149
{
1150
  gunichar *nuni;
1151
  gint nlen;
1152
  gunichar unival;
1153
  const gchar *p;
1154
1155
  if (strlen (string) == 0) return NULL;
1156
1157
  nuni = g_malloc (sizeof (gunichar) * (strlen (string) + 1));
1158
1159
  nlen = 0;
1160
  for (p = e_util_unicode_get_utf8 (string, &unival);
1161
       p && unival;
1162
       p = e_util_unicode_get_utf8 (p, &unival)) {
1163
      gunichar sc;
1164
      sc = stripped_char (unival);
1165
      if (sc) {
1166
          nuni[nlen++] = sc;
1167
      }
1168
  }
1169
1170
  /* NULL means there was illegal utf-8 sequence */
1171
  if (!p) nlen = 0;
1172
1173
  nuni[nlen] = 0;
1174
1175
  return nuni;
1176
}
1177
1178
static gboolean
1179
hildon_live_search_visible_func (GtkTreeModel *model,
1180
                                 GtkTreeIter *iter,
1181
                                 gchar *prefix,
1182
                                 gpointer userdata)
1183
{
1184
  gboolean visible = TRUE;
1185
  gchar *string;
1186
  GSList *list_iter;
1187
  HildonTouchSelectorColumn *col;
1188
  HildonTouchSelector *selector;
1189
1190
  col = HILDON_TOUCH_SELECTOR_COLUMN (userdata);
1191
  selector = col->priv->parent;
1192
  gint text_column = GPOINTER_TO_INT (col->priv->text_column);
1193
1194
  gtk_tree_model_get (model, iter, text_column, &string, -1);
1195
  list_iter = selector->priv->norm_tokens;
1196
  while (visible && list_iter) {
1197
    visible = (string != NULL &&
1198
               e_util_utf8_strstrcasedecomp_needle_stripped (string,
1199
                                                             (gunichar *)list_iter->data) != NULL);
1200
    list_iter = list_iter->next;
1201
  }
1202
1203
  g_free (string);
1204
1205
  return visible;
1206
}
1207
1208
static gboolean
1209
on_live_search_refilter (HildonLiveSearch *livesearch,
1210
                         gpointer userdata)
1211
{
1212
    HildonTouchSelector *selector = HILDON_TOUCH_SELECTOR (userdata);
1213
1214
    gchar **tokens = g_strsplit (hildon_live_search_get_text (livesearch), " ", -1);
1215
    gunichar *token;
1216
    gint i;
1217
1218
    if (selector->priv->norm_tokens != NULL) {
1219
        g_slist_foreach (selector->priv->norm_tokens, (GFunc) g_free, NULL);
1220
        g_slist_free (selector->priv->norm_tokens);
1221
        selector->priv->norm_tokens = NULL;
1222
    }
1223
1224
    for (i = 0; tokens [i] != NULL; i++) {
1225
        token = strip_string (tokens[i]);
1226
        if (token != NULL)
1227
            selector->priv->norm_tokens = g_slist_prepend (selector->priv->norm_tokens,
1228
                                                           token);
1229
    }
1230
1231
    g_strfreev (tokens);
1232
1233
    return FALSE;
1234
}
1235
1236
/**
1237
 * hildon_touch_selector_column_set_text_column:
1238
 * @column: A #HildonTouchSelectorColumn
1239
 * @text_column: the index of a model column in the model for @column.
1240
 *
1241
 * Sets the model column to be displayed in @column. @text_column must point to a
1242
 * column in the model used with type %G_TYPE_STRING. Initially, this property
1243
 * is unset. You should set it before using the #HildonTouchSelector that uses
1244
 * @column.
1245
 *
1246
 * Since: 2.2
1247
 **/
1248
void
1249
hildon_touch_selector_column_set_text_column (HildonTouchSelectorColumn *column,
1250
                                              gint text_column)
1251
{
1252
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (column));
1253
  g_return_if_fail (text_column >= -1);
1254
1255
  column->priv->text_column = text_column;
1256
1257
  if (column->priv->livesearch) {
1258
    hildon_live_search_set_visible_func (HILDON_LIVE_SEARCH (column->priv->livesearch),
1259
                                         hildon_live_search_visible_func,
1260
                                         column,
1261
                                         NULL);
1262
  }
1263
1264
  g_object_notify (G_OBJECT (column), "text-column");
1265
}
1266
1267
/**
1268
 * hildon_touch_selector_column_get_text_column:
1269
 * @column: a #HildonTouchSelectorColumn
1270
 *
1271
 * Gets the model column set as the text source for @column.
1272
 *
1273
 * Returns: the index of the text column for @column, or -1 if unset.
1274
 *
1275
 * Since: 2.2
1276
 **/
1277
gint
1278
hildon_touch_selector_column_get_text_column (HildonTouchSelectorColumn *column)
1279
{
1280
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (column), -1);
1281
1282
  return column->priv->text_column;
1283
}
1284
1285
static void
1286
hildon_touch_selector_column_get_property (GObject *object, guint property_id,
1287
                                           GValue *value, GParamSpec *pspec)
1288
{
1289
  switch (property_id) {
1290
  case PROP_TEXT_COLUMN:
1291
    g_value_set_int (value,
1292
                     hildon_touch_selector_column_get_text_column (HILDON_TOUCH_SELECTOR_COLUMN (object)));
1293
    break;
1294
  default:
1295
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1296
  }
1297
}
1298
1299
static void
1300
hildon_touch_selector_column_set_property (GObject *object, guint property_id,
1301
                                           const GValue *value, GParamSpec *pspec)
1302
{
1303
  switch (property_id) {
1304
  case PROP_TEXT_COLUMN:
1305
    hildon_touch_selector_column_set_text_column (HILDON_TOUCH_SELECTOR_COLUMN (object),
1306
                                                  g_value_get_int (value));
1307
    break;
1308
  default:
1309
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1310
  }
1311
}
1312
1313
static void
1314
hildon_touch_selector_column_dispose      (GObject *object)
1315
{
1316
  HildonTouchSelectorColumnPrivate *priv =
1317
      HILDON_TOUCH_SELECTOR_COLUMN (object)->priv;
1318
1319
  if (priv->model != NULL) {
1320
      g_object_unref (priv->model);
1321
      priv->model = NULL;
1322
  }
1323
1324
  if (priv->filter != NULL) {
1325
      g_object_unref (priv->filter);
1326
      priv->filter = NULL;
1327
  }
1328
1329
  G_OBJECT_CLASS (hildon_touch_selector_column_parent_class)->dispose (object);
1330
}
1331
1332
static void
1333
hildon_touch_selector_column_finalize     (GObject *object)
1334
{
1335
  HildonTouchSelectorColumnPrivate *priv = HILDON_TOUCH_SELECTOR_COLUMN (object)->priv;
1336
1337
  if (priv->initial_path) {
1338
    gtk_tree_path_free (priv->initial_path);
1339
  }
1340
1341
  G_OBJECT_CLASS (hildon_touch_selector_column_parent_class)->finalize (object);
1342
}
1343
1344
/* ------------------------ GtkCellLayout implementation -------------------- */
1345
static void
1346
hildon_touch_selector_column_cell_layout_init (GtkCellLayoutIface      *iface)
1347
{
1348
  iface->pack_start         = hildon_touch_selector_column_cell_layout_pack_start;
1349
  iface->pack_end           = hildon_touch_selector_column_cell_layout_pack_end;
1350
  iface->clear              = hildon_touch_selector_column_cell_layout_clear;
1351
  iface->add_attribute      = hildon_touch_selector_column_cell_layout_add_attribute;
1352
  iface->set_cell_data_func = hildon_touch_selector_column_cell_layout_set_cell_data_func;
1353
  iface->clear_attributes   = hildon_touch_selector_column_cell_layout_clear_attributes;
1354
  iface->reorder            = hildon_touch_selector_column_cell_layout_reorder;
1355
  iface->get_cells          = hildon_touch_selector_column_cell_layout_get_cells;
1356
}
1357
1358
static void
1359
hildon_touch_selector_column_cell_layout_pack_start (GtkCellLayout         *cell_layout,
1360
                                               GtkCellRenderer       *cell,
1361
                                               gboolean               expand)
1362
{
1363
  HildonTouchSelectorColumn *sel_column = NULL;
1364
  GtkTreeViewColumn *view_column = NULL;
1365
1366
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1367
  sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1368
1369
  view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1370
1371
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(view_column), cell, expand);
1372
1373
}
1374
1375
static void
1376
hildon_touch_selector_column_cell_layout_pack_end (GtkCellLayout         *cell_layout,
1377
                                             GtkCellRenderer       *cell,
1378
                                             gboolean               expand)
1379
{
1380
  HildonTouchSelectorColumn *sel_column = NULL;
1381
  GtkTreeViewColumn *view_column = NULL;
1382
1383
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1384
  sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1385
1386
  view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1387
1388
  gtk_cell_layout_pack_end (GTK_CELL_LAYOUT(view_column), cell, expand);
1389
}
1390
1391
static void
1392
hildon_touch_selector_column_cell_layout_clear (GtkCellLayout         *cell_layout)
1393
{
1394
  HildonTouchSelectorColumn *sel_column = NULL;
1395
  GtkTreeViewColumn *view_column = NULL;
1396
1397
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1398
  sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1399
1400
  view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1401
1402
  gtk_cell_layout_clear (GTK_CELL_LAYOUT(view_column));
1403
}
1404
1405
static void
1406
hildon_touch_selector_column_cell_layout_add_attribute (GtkCellLayout         *cell_layout,
1407
                                                  GtkCellRenderer       *cell,
1408
                                                  const gchar           *attribute,
1409
                                                  gint                   column)
1410
{
1411
  HildonTouchSelectorColumn *sel_column = NULL;
1412
  GtkTreeViewColumn *view_column = NULL;
1413
1414
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1415
  sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1416
1417
  view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1418
1419
  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(view_column), cell, attribute, column);
1420
}
1421
1422
1423
static void
1424
hildon_touch_selector_column_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
1425
                                                       GtkCellRenderer       *cell,
1426
                                                       GtkCellLayoutDataFunc  func,
1427
                                                       gpointer               func_data,
1428
                                                       GDestroyNotify         destroy)
1429
{
1430
  HildonTouchSelectorColumn *sel_column = NULL;
1431
  GtkTreeViewColumn *view_column = NULL;
1432
1433
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1434
  sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1435
1436
  view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1437
1438
  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(view_column), cell, func,
1439
                                      func_data, destroy);
1440
}
1441
1442
static void
1443
hildon_touch_selector_column_cell_layout_clear_attributes (GtkCellLayout         *cell_layout,
1444
                                                     GtkCellRenderer       *cell)
1445
{
1446
  HildonTouchSelectorColumn *sel_column = NULL;
1447
  GtkTreeViewColumn *view_column = NULL;
1448
1449
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1450
  sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1451
1452
  view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1453
1454
  gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (view_column), cell);
1455
}
1456
1457
static void
1458
hildon_touch_selector_column_cell_layout_reorder (GtkCellLayout         *cell_layout,
1459
                                            GtkCellRenderer       *cell,
1460
                                            gint                   position)
1461
{
1462
  HildonTouchSelectorColumn *sel_column = NULL;
1463
  GtkTreeViewColumn *view_column = NULL;
1464
1465
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout));
1466
  sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1467
1468
  view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1469
1470
  gtk_cell_layout_reorder (GTK_CELL_LAYOUT(view_column), cell, position);
1471
}
1472
1473
static GList*
1474
hildon_touch_selector_column_cell_layout_get_cells (GtkCellLayout         *cell_layout)
1475
{
1476
  HildonTouchSelectorColumn *sel_column = NULL;
1477
  GtkTreeViewColumn *view_column = NULL;
1478
1479
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR_COLUMN (cell_layout), NULL);
1480
  sel_column = HILDON_TOUCH_SELECTOR_COLUMN (cell_layout);
1481
1482
  view_column = gtk_tree_view_get_column (sel_column->priv->tree_view, 0);
1483
1484
  return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(view_column));
1485
}
1486
1487
/* ------------------------------ PUBLIC METHODS ---------------------------- */
1488
1489
/**
1490
 * hildon_touch_selector_new:
1491
 *
1492
 * Creates a new empty #HildonTouchSelector.
1493
 *
1494
 * Returns: a new #HildonTouchSelector.
1495
 *
1496
 * Since: 2.2
1497
 **/
1498
GtkWidget *
1499
hildon_touch_selector_new (void)
1500
{
1501
  return g_object_new (HILDON_TYPE_TOUCH_SELECTOR, NULL);
1502
}
1503
1504
/**
1505
 * hildon_touch_selector_new_text:
1506
 *
1507
 * Creates a #HildonTouchSelector with a single text column that
1508
 * can be populated conveniently through hildon_touch_selector_append_text(),
1509
 * hildon_touch_selector_prepend_text(), hildon_touch_selector_insert_text().
1510
 *
1511
 * Returns: A new #HildonTouchSelector
1512
 *
1513
 * Since: 2.2
1514
 **/
1515
GtkWidget *
1516
hildon_touch_selector_new_text (void)
1517
{
1518
  GtkWidget *selector;
1519
  GtkListStore *store;
1520
1521
  selector = hildon_touch_selector_new ();
1522
  store = gtk_list_store_new (1, G_TYPE_STRING);
1523
1524
  hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
1525
                                            GTK_TREE_MODEL (store), TRUE);
1526
1527
  g_object_unref (store);
1528
1529
  return selector;
1530
}
1531
1532
/**
1533
 * hildon_touch_selector_append_text:
1534
 * @selector: A #HildonTouchSelector.
1535
 * @text: a non %NULL text string.
1536
 *
1537
 * Appends a new entry in a #HildonTouchSelector created with
1538
 * hildon_touch_selector_new_text().
1539
 *
1540
 * Since: 2.2
1541
 **/
1542
void
1543
hildon_touch_selector_append_text (HildonTouchSelector * selector,
1544
                                   const gchar * text)
1545
{
1546
  GtkTreeModel *model;
1547
1548
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1549
  g_return_if_fail (text != NULL);
1550
1551
  model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
1552
1553
  g_return_if_fail (GTK_IS_LIST_STORE (model));
1554
1555
  gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
1556
                                     NULL, G_MAXINT, 0, text, -1);
1557
}
1558
1559
/**
1560
 * hildon_touch_selector_prepend_text:
1561
 * @selector: A #HildonTouchSelector.
1562
 * @text: a non %NULL text string.
1563
 *
1564
 * Prepends a new entry in a #HildonTouchSelector created with
1565
 * hildon_touch_selector_new_text().
1566
 *
1567
 * Since: 2.2
1568
 **/
1569
void
1570
hildon_touch_selector_prepend_text (HildonTouchSelector * selector,
1571
                                    const gchar * text)
1572
{
1573
  GtkTreeModel *model;
1574
1575
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1576
  g_return_if_fail (text != NULL);
1577
1578
  model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
1579
1580
  g_return_if_fail (GTK_IS_LIST_STORE (model));
1581
1582
  gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
1583
                                     NULL, 0, 0, text, -1);
1584
}
1585
1586
/**
1587
 * hildon_touch_selector_insert_text:
1588
 * @selector: a #HildonTouchSelector.
1589
 * @position: the position to insert @text.
1590
 * @text: A non %NULL text string.
1591
 *
1592
 * Inserts a new entry in a particular position of a
1593
 * #HildonTouchSelector created with hildon_touch_selector_new_text().
1594
 *
1595
 * Since: 2.2
1596
 **/
1597
void
1598
hildon_touch_selector_insert_text (HildonTouchSelector * selector,
1599
                                   gint position, const gchar * text)
1600
{
1601
  GtkTreeModel *model;
1602
1603
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1604
  g_return_if_fail (text != NULL);
1605
  g_return_if_fail (position >= 0);
1606
1607
  model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
1608
1609
  g_return_if_fail (GTK_IS_LIST_STORE (model));
1610
1611
  gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
1612
                                     NULL, position, 0, text, -1);
1613
}
1614
1615
static void
1616
hildon_touch_selector_add_live_search (HildonTouchSelector *selector,
1617
                                       HildonTouchSelectorColumn *column)
1618
{
1619
  if (column->priv->livesearch == NULL) {
1620
    gint text_column;
1621
1622
    column->priv->livesearch = hildon_live_search_new ();
1623
    hildon_live_search_set_filter (HILDON_LIVE_SEARCH (column->priv->livesearch),
1624
                                   GTK_TREE_MODEL_FILTER (column->priv->filter));
1625
    g_signal_connect (column->priv->livesearch, "refilter",
1626
                      G_CALLBACK (on_live_search_refilter), selector);
1627
    gtk_box_pack_start (GTK_BOX (column->priv->vbox),
1628
                        column->priv->livesearch,
1629
                        FALSE, FALSE, 0);
1630
    hildon_live_search_widget_hook (HILDON_LIVE_SEARCH (column->priv->livesearch),
1631
                                    GTK_WIDGET (column->priv->vbox),
1632
                                    GTK_WIDGET (column->priv->tree_view));
1633
    text_column = hildon_touch_selector_column_get_text_column (column);
1634
    if (text_column > -1) {
1635
      hildon_live_search_set_visible_func (HILDON_LIVE_SEARCH (column->priv->livesearch),
1636
                                           hildon_live_search_visible_func,
1637
                                           column,
1638
                                           NULL);
1639
    }
1640
  }
1641
}
1642
1643
static void
1644
hildon_touch_selector_remove_live_search (HildonTouchSelector *selector)
1645
{
1646
  HildonTouchSelectorColumn *col;
1647
1648
  if (selector->priv->has_live_search == FALSE ||
1649
      selector->priv->columns == NULL)
1650
    return;
1651
1652
  col = (HildonTouchSelectorColumn *) selector->priv->columns->data;
1653
1654
  if (col->priv->livesearch != NULL) {
1655
    hildon_live_search_widget_unhook (HILDON_LIVE_SEARCH (col->priv->livesearch));
1656
    gtk_widget_destroy (col->priv->livesearch);
1657
    col->priv->livesearch = NULL;
1658
  }
1659
1660
  selector->priv->has_live_search = FALSE;
1661
}
1662
1663
/**
1664
 * hildon_touch_selector_append_column
1665
 * @selector: a #HildonTouchSelector
1666
 * @model: the #GtkTreeModel with the data of the column
1667
 * @cell_renderer: The #GtkCellRenderer where to draw each row contents.
1668
 * @Varargs: a %NULL-terminated pair of attributes and column numbers.
1669
 *
1670
 * This functions adds a new column to the widget, whose data will
1671
 * be obtained from the model. Only widgets added this way should used on
1672
 * the selection logic, the print function, the #HildonTouchSelector::changed
1673
 * signal, etc. Internally, a #GtkTreeView will be added to the widget, using
1674
 * @model as the data source. This will add a reference to @model, so you
1675
 * must unref it after calling this function if you are not going to
1676
 * use it anymore.
1677
 *
1678
 * You can optionally pass a #GtkCellRenderer in @cell_renderer,
1679
 * together with a %NULL-terminated list of pairs property/value, in
1680
 * the same way you would use gtk_tree_view_column_set_attributes().
1681
 * This will pack @cell_renderer at the start of the column, expanded
1682
 * by default.  If you prefer not to add it this way, you can simply
1683
 * pass %NULL to @cell_renderer and use the #GtkCellLayout interface
1684
 * on the returned #HildonTouchSelectorColumn to set your
1685
 * renderers. Please note that the returned #HildonTouchSelectorColumn
1686
 * is owned by @selector, you shouldn't unref it after setting it
1687
 * up.
1688
 *
1689
 * Initially, the returned #HildonTouchSelectorColumn will have its
1690
 * #HildonTouchSelectorColumn:text-column property unset. You should set
1691
 * it to a column in @model with type %G_TYPE_STRING. See
1692
 * hildon_touch_selector_column_set_text_column().
1693
 *
1694
 * This method could change the current #HildonTouchSelector:hildon-ui-mode.
1695
 * %HILDON_UI_MODE_NORMAL is only allowed with one column, so if the selector
1696
 * is in this mode, and a additional column is added,
1697
 * #HildonTouchSelector:hildon-ui-mode will change to %HILDON_UI_MODE_EDIT.
1698
 *
1699
 * Returns: the new column added added, %NULL otherwise.
1700
 *
1701
 * Since: 2.2
1702
 **/
1703
1704
HildonTouchSelectorColumn*
1705
hildon_touch_selector_append_column (HildonTouchSelector * selector,
1706
                                     GtkTreeModel * model,
1707
                                     GtkCellRenderer * cell_renderer, ...)
1708
{
1709
  va_list args;
1710
  HildonTouchSelectorColumn *new_column = NULL;
1711
  gboolean emit_changed = FALSE;
1712
  gint colnum;
1713
1714
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1715
  g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
1716
1717
  if (model != NULL) {
1718
1719
    va_start (args, cell_renderer);
1720
    new_column = _create_new_column (selector, model, &emit_changed, cell_renderer, args);
1721
    va_end (args);
1722
1723
    /* If we already have one column, disable live search */
1724
    if (selector->priv->has_live_search &&
1725
        selector->priv->columns != NULL &&
1726
        selector->priv->columns->next == NULL) {
1727
	    hildon_touch_selector_remove_live_search (selector);
1728
    }
1729
1730
    selector->priv->columns = g_slist_append (selector->priv->columns,
1731
                                              new_column);
1732
1733
    new_column->priv->vbox = gtk_vbox_new (FALSE, 0);
1734
    gtk_box_pack_start (GTK_BOX (new_column->priv->vbox),
1735
                        new_column->priv->panarea,
1736
                        TRUE, TRUE, 0);
1737
    gtk_box_pack_start (GTK_BOX (selector->priv->hbox),
1738
                        new_column->priv->vbox,
1739
                        TRUE, TRUE, 6);
1740
1741
    if (selector->priv->has_live_search) {
1742
      hildon_touch_selector_add_live_search (selector, new_column);
1743
    }
1744
1745
    gtk_widget_show_all (new_column->priv->vbox);
1746
1747
    gtk_widget_child_focus (GTK_WIDGET (selector),
1748
                            GTK_DIR_TAB_FORWARD);
1749
1750
    if (selector->priv->initial_scroll) {
1751
      _hildon_touch_selector_center_on_selected_items (selector, new_column);
1752
    }
1753
1754
  } else {
1755
    return NULL;
1756
  }
1757
1758
  g_signal_emit (selector, hildon_touch_selector_signals[COLUMNS_CHANGED], 0);
1759
  if (emit_changed) {
1760
    colnum = g_slist_length (selector->priv->columns);
1761
    hildon_touch_selector_emit_value_changed (selector, colnum);
1762
  }
1763
1764
  hildon_touch_selector_check_ui_mode_coherence (selector);
1765
1766
  return new_column;
1767
}
1768
1769
/**
1770
 * hildon_touch_selector_append_text_column
1771
 * @selector: a #HildonTouchSelector
1772
 * @model: a #GtkTreeModel with data for the column
1773
 * @center: whether to center the text on the column
1774
 *
1775
 * Equivalent to hildon_touch_selector_append_column(), but using a
1776
 * default text cell renderer. This is the most common use case of the
1777
 * widget.
1778
 *
1779
 * Note that this call adds a reference to @model, so you must unref
1780
 * it afterwards if you are not going to use it anymore.
1781
 *
1782
 * Returns: the new column added, NULL otherwise.
1783
 *
1784
 * Since: 2.2
1785
 **/
1786
HildonTouchSelectorColumn*
1787
hildon_touch_selector_append_text_column (HildonTouchSelector * selector,
1788
                                          GtkTreeModel * model, gboolean center)
1789
{
1790
  gfloat xalign = center ? 0.5 : 0.0;
1791
  GtkCellRenderer *renderer;
1792
  HildonTouchSelectorColumn *column = NULL;
1793
1794
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
1795
  g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
1796
1797
  renderer = gtk_cell_renderer_text_new ();
1798
1799
  g_object_set (renderer,
1800
                "width", 1,
1801
                "xalign", xalign,
1802
                NULL);
1803
1804
  column = hildon_touch_selector_append_column (selector, model, renderer,
1805
                                                "text", 0, NULL);
1806
  hildon_touch_selector_column_set_text_column (column, 0);
1807
1808
  g_object_set (column->priv->panarea, "sps", 28, NULL);
1809
1810
  return column;
1811
}
1812
1813
/**
1814
 * hildon_touch_selector_remove_column:
1815
 * @selector: a #HildonTouchSelector
1816
 * @column: the position of the column to be removed
1817
 *
1818
 * Removes a column from @selector.
1819
 *
1820
 * Returns: %TRUE if the column was removed, %FALSE otherwise
1821
 *
1822
 * Since: 2.2
1823
 **/
1824
gboolean
1825
hildon_touch_selector_remove_column (HildonTouchSelector * selector, gint column)
1826
{
1827
  HildonTouchSelectorColumn *current_column = NULL;
1828
  HildonTouchSelectorPrivate *priv;
1829
1830
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
1831
  g_return_val_if_fail (column <
1832
                        hildon_touch_selector_get_num_columns (selector), FALSE);
1833
1834
  priv = HILDON_TOUCH_SELECTOR_GET_PRIVATE (selector);
1835
  current_column = g_slist_nth_data (priv->columns, column);
1836
1837
  gtk_container_remove (GTK_CONTAINER (priv->hbox), current_column->priv->vbox);
1838
  priv->columns = g_slist_remove (priv->columns, current_column);
1839
  g_object_unref (current_column);
1840
1841
  g_signal_emit (selector, hildon_touch_selector_signals[COLUMNS_CHANGED], 0);
1842
1843
  return TRUE;
1844
}
1845
1846
/**
1847
 * hildon_touch_selector_set_column_attributes:
1848
 * @selector: a #HildonTouchSelector
1849
 * @num_column: the number of the column whose attributes we're setting
1850
 * @cell_renderer: the #GtkCellRendere we're setting the attributes of
1851
 * @Varargs: A %NULL-terminated list of attributes.
1852
 *
1853
 * Sets the attributes for the given column. The attributes must be given
1854
 * in attribute/column pairs, just like in gtk_tree_view_column_set_attributes().
1855
 * All existing attributes are removed and replaced with the new ones.
1856
 *
1857
 * Deprecated: #HildonTouchSelectorColumn implements #GtkCellLayout, use this
1858
 *             interface instead. See
1859
 *             hildon_touch_selector_get_column().
1860
 *
1861
 * Since: 2.2
1862
 **/
1863
void
1864
hildon_touch_selector_set_column_attributes (HildonTouchSelector * selector,
1865
                                             gint num_column,
1866
                                             GtkCellRenderer * cell_renderer,
1867
                                             ...)
1868
{
1869
  va_list args;
1870
  GtkTreeViewColumn *tree_column = NULL;
1871
  HildonTouchSelectorColumn *current_column = NULL;
1872
  gchar *attribute = NULL;
1873
  gint value = 0;
1874
1875
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1876
  g_return_if_fail (num_column <
1877
                    hildon_touch_selector_get_num_columns (selector));
1878
1879
  current_column = g_slist_nth_data (selector->priv->columns, num_column);
1880
1881
  tree_column = gtk_tree_view_get_column (current_column->priv->tree_view, 0);
1882
  gtk_tree_view_remove_column (current_column->priv->tree_view, tree_column);
1883
1884
  tree_column = gtk_tree_view_column_new ();
1885
  gtk_tree_view_column_pack_start (tree_column, cell_renderer, TRUE);
1886
1887
  va_start (args, cell_renderer);
1888
  attribute = va_arg (args, gchar *);
1889
1890
  gtk_tree_view_column_clear_attributes (tree_column, cell_renderer);
1891
1892
  while (attribute != NULL) {
1893
    value = va_arg (args, gint);
1894
    gtk_tree_view_column_add_attribute (tree_column, cell_renderer,
1895
                                        attribute, value);
1896
    attribute = va_arg (args, gchar *);
1897
  }
1898
1899
  va_end (args);
1900
1901
  gtk_tree_view_append_column (current_column->priv->tree_view, tree_column);
1902
}
1903
1904
/**
1905
 * hildon_touch_selector_get_num_columns:
1906
 * @selector: a #HildonTouchSelector
1907
 *
1908
 * Gets the number of columns in the #HildonTouchSelector.
1909
 *
1910
 * Returns: the number of columns in @selector.
1911
 *
1912
 * Since: 2.2
1913
 **/
1914
gint
1915
hildon_touch_selector_get_num_columns (HildonTouchSelector * selector)
1916
{
1917
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), -1);
1918
1919
  return g_slist_length (selector->priv->columns);
1920
}
1921
1922
/**
1923
 * hildon_touch_selector_get_column_selection_mode:
1924
 * @selector: a #HildonTouchSelector
1925
 *
1926
 * Gets the selection mode of @selector.
1927
 *
1928
 * Returns: one of #HildonTouchSelectorSelectionMode
1929
 *
1930
 * Since: 2.2
1931
 **/
1932
HildonTouchSelectorSelectionMode
1933
hildon_touch_selector_get_column_selection_mode (HildonTouchSelector * selector)
1934
{
1935
  HildonTouchSelectorSelectionMode result =
1936
    HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
1937
  GtkSelectionMode treeview_mode = GTK_SELECTION_BROWSE;
1938
  HildonTouchSelectorColumn *column = NULL;
1939
  GtkTreeSelection *selection = NULL;
1940
1941
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), result);
1942
  g_return_val_if_fail (hildon_touch_selector_get_num_columns (selector) > 0,
1943
                        result);
1944
1945
  column = HILDON_TOUCH_SELECTOR_COLUMN (selector->priv->columns->data);
1946
1947
  selection = gtk_tree_view_get_selection (column->priv->tree_view);
1948
  treeview_mode = gtk_tree_selection_get_mode (selection);
1949
1950
1951
  if (treeview_mode == GTK_SELECTION_MULTIPLE) {
1952
    result = HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE;
1953
  } else {
1954
    result = HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE;
1955
  }
1956
1957
  return result;
1958
}
1959
1960
/**
1961
 * hildon_touch_selector_set_column_selection_mode:
1962
 * @selector: a #HildonTouchSelector
1963
 * @mode: the #HildonTouchSelectorMode for @selector
1964
 *
1965
 * Sets the selection mode for @selector. See #HildonTouchSelectorSelectionMode.
1966
 *
1967
 * The new @mode will be set, but take into into account that the
1968
 * #HildonTouchSelectorSelectionMode is ignored if the @selector
1969
 * #HildonTouchSelector:hildon-ui-mode property is set to %HILDON_UI_MODE_NORMAL
1970
 *
1971
 * Since: 2.2
1972
 **/
1973
void
1974
hildon_touch_selector_set_column_selection_mode (HildonTouchSelector * selector,
1975
                                                 HildonTouchSelectorSelectionMode mode)
1976
{
1977
  GtkTreeView *tv = NULL;
1978
  HildonTouchSelectorColumn *column = NULL;
1979
  GtkTreeSelection *selection = NULL;
1980
  GtkSelectionMode treeview_mode = GTK_SELECTION_MULTIPLE;
1981
  GtkTreeIter iter;
1982
  HildonTouchSelectorSelectionMode current_mode;
1983
1984
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
1985
  g_return_if_fail (hildon_touch_selector_get_num_columns (selector) > 0);
1986
1987
  current_mode = hildon_touch_selector_get_column_selection_mode (selector);
1988
1989
  if (current_mode == mode) {
1990
    return;
1991
  }
1992
1993
  column = HILDON_TOUCH_SELECTOR_COLUMN ((g_slist_nth (selector->priv->columns, 0))->data);
1994
  tv = column->priv->tree_view;
1995
1996
  if (tv) {
1997
    switch (mode) {
1998
    case HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE:
1999
      treeview_mode = GTK_SELECTION_BROWSE;
2000
      break;
2001
    case HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE:
2002
      treeview_mode = GTK_SELECTION_MULTIPLE;
2003
      break;
2004
    }
2005
2006
    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
2007
    gtk_tree_selection_set_mode (selection, treeview_mode);
2008
2009
    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
2010
    gtk_tree_selection_unselect_all (selection);
2011
    if (gtk_tree_model_get_iter_first (column->priv->filter, &iter)) {
2012
        gtk_tree_selection_select_iter (selection, &iter);
2013
    }
2014
2015
    /* the column changed was the first one */
2016
    hildon_touch_selector_emit_value_changed (selector, 0);
2017
  }
2018
2019
}
2020
2021
/**
2022
 * hildon_touch_selector_set_print_func:
2023
 * @selector: a #HildonTouchSelector
2024
 * @func: a #HildonTouchSelectorPrintFunc function
2025
 *
2026
 * Sets the function to be used by hildon_touch_selector_get_current_text().
2027
 * See hildon_touch_selector_set_print_func_full().
2028
 *
2029
 * Since: 2.2
2030
 **/
2031
void
2032
hildon_touch_selector_set_print_func (HildonTouchSelector * selector,
2033
                                      HildonTouchSelectorPrintFunc func)
2034
{
2035
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2036
2037
  hildon_touch_selector_set_print_func_full (selector, func, NULL, NULL);
2038
}
2039
2040
/**
2041
 * hildon_touch_selector_set_print_func_full:
2042
 * @selector: a #HildonTouchSelector
2043
 * @func: a #HildonTouchSelectorPrintFunc function
2044
 * @user_data: a pointer to user data or %NULL
2045
 * @destroy_func: a callback for freeing the user data or %NULL
2046
 *
2047
 * Sets the function to be used by hildon_touch_selector_get_current_text()
2048
 * to produce a text representation of the currently selected items in @selector.
2049
 * The default function will return a concatenation of comma separated items
2050
 * selected in each column in @selector. Use this to override this method if you
2051
 * need a particular representation for your application.
2052
 *
2053
 * Since: 2.2
2054
 **/
2055
void
2056
hildon_touch_selector_set_print_func_full (HildonTouchSelector          *selector,
2057
                                           HildonTouchSelectorPrintFunc  func,
2058
                                           gpointer                      user_data,
2059
                                           GDestroyNotify                destroy_func)
2060
{
2061
  gpointer       old_user_data;
2062
  GDestroyNotify old_destroy_func;
2063
2064
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2065
2066
  old_user_data = selector->priv->print_user_data;
2067
  old_destroy_func = selector->priv->print_destroy_func;
2068
2069
  selector->priv->print_func = func;
2070
  selector->priv->print_user_data = user_data;
2071
  selector->priv->print_destroy_func = destroy_func;
2072
2073
  if (old_destroy_func && old_user_data != user_data)
2074
    (*old_destroy_func) (old_user_data);
2075
}
2076
2077
/**
2078
 * hildon_touch_selector_get_print_func:
2079
 * @selector: a #HildonTouchSelector
2080
 *
2081
 * Gets the #HildonTouchSelectorPrintFunc currently used. See
2082
 * hildon_touch_selector_set_print_func().
2083
 *
2084
 * Returns: a #HildonTouchSelectorPrintFunc or %NULL if the default
2085
 * one is currently used.
2086
 **/
2087
HildonTouchSelectorPrintFunc
2088
hildon_touch_selector_get_print_func (HildonTouchSelector * selector)
2089
{
2090
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
2091
2092
  return selector->priv->print_func;
2093
}
2094
2095
/**
2096
 * hildon_touch_selector_set_active:
2097
 * @selector: a #HildonTouchSelector
2098
 * @column: column number
2099
 * @index: the index of the item to select, or -1 to have no active item
2100
 *
2101
 * Sets the active item of the #HildonTouchSelector to @index. The
2102
 * column number is taken from @column.
2103
 *
2104
 * @selector must be in %HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE
2105
 *
2106
 * Since: 2.2
2107
 **/
2108
void
2109
hildon_touch_selector_set_active                (HildonTouchSelector *selector,
2110
                                                 gint                 column,
2111
                                                 gint                 index)
2112
{
2113
  GtkTreeSelection *selection = NULL;
2114
  HildonTouchSelectorColumn *current_column = NULL;
2115
  HildonTouchSelectorSelectionMode mode;
2116
  GtkTreePath *path, *filter_path;
2117
2118
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2119
  g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
2120
  mode = hildon_touch_selector_get_column_selection_mode (selector);
2121
  g_return_if_fail (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE);
2122
2123
  current_column = g_slist_nth_data (selector->priv->columns, column);
2124
2125
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
2126
2127
  if (index == -1) {
2128
      gtk_tree_selection_unselect_all (selection);
2129
      hildon_touch_selector_emit_value_changed (selector, column);
2130
      return;
2131
  }
2132
2133
  path = gtk_tree_path_new_from_indices (index, -1);
2134
  filter_path = gtk_tree_model_filter_convert_child_path_to_path (
2135
      GTK_TREE_MODEL_FILTER (current_column->priv->filter), path);
2136
2137
  if (filter_path != NULL) {
2138
      gtk_tree_selection_unselect_all (selection);
2139
      gtk_tree_selection_select_path (selection, filter_path);
2140
2141
      hildon_touch_selector_emit_value_changed (selector, column);
2142
      gtk_tree_path_free (filter_path);
2143
  }
2144
  gtk_tree_path_free (path);
2145
}
2146
2147
/**
2148
 * hildon_touch_selector_get_active:
2149
 * @selector: a #HildonTouchSelector
2150
 * @column: column number
2151
 *
2152
 * Returns the index of the currently active item in column number
2153
 * @column, or -1 if there's no active item.
2154
 *
2155
 * @selector must be in %HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE
2156
 *
2157
 * Returns: an integer which is the index of the currently active
2158
 * item, or -1 if there's no active item.
2159
 *
2160
 * Since: 2.2
2161
 **/
2162
gint
2163
hildon_touch_selector_get_active                (HildonTouchSelector *selector,
2164
                                                 gint                 column)
2165
{
2166
  GtkTreeSelection *selection = NULL;
2167
  HildonTouchSelectorColumn *current_column = NULL;
2168
  HildonTouchSelectorSelectionMode mode;
2169
  GtkTreeIter filter_iter;
2170
  gint index = -1;
2171
2172
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), -1);
2173
  g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector), -1);
2174
2175
  mode = hildon_touch_selector_get_column_selection_mode (selector);
2176
  g_return_val_if_fail (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE, -1);
2177
2178
  current_column = g_slist_nth_data (selector->priv->columns, column);
2179
2180
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
2181
2182
  if (gtk_tree_selection_get_selected (selection, NULL, &filter_iter)) {
2183
    GtkTreePath *path;
2184
    GtkTreeIter iter;
2185
    gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (current_column->priv->filter),
2186
                                                      &iter, &filter_iter);
2187
    path = gtk_tree_model_get_path (current_column->priv->model, &iter);
2188
    index = (gtk_tree_path_get_indices (path))[0];
2189
    gtk_tree_path_free (path);
2190
  }
2191
2192
  return index;
2193
}
2194
2195
/**
2196
 * hildon_touch_selector_get_selected:
2197
 * @selector: a #HildonTouchSelector
2198
 * @column: the column to get the item from
2199
 * @iter: #GtkTreeIter currently selected
2200
 *
2201
 * Sets @iter to the currently selected node on the nth-column, if selection is
2202
 * set to %HILDON_TOUCH_SELECTOR_SINGLE or %HILDON_TOUCH_SELECTOR_MULTIPLE with
2203
 * a column different that the first one. @iter may be %NULL if you just want to
2204
 * test if selection has any selected items.
2205
 *
2206
 * This function will not work if selection is in
2207
 * %HILDON_TOUCH_SELECTOR_MULTIPLE mode and the column is the first one.
2208
 *
2209
 * To change the currently selected iter, see
2210
 * hildon_touch_selector_select_iter()
2211
 *
2212
 * See also gtk_tree_selection_get_selected() for more information.
2213
 *
2214
 * Returns: %TRUE if @iter was correctly set, %FALSE otherwise
2215
 *
2216
 * Since: 2.2
2217
 **/
2218
gboolean
2219
hildon_touch_selector_get_selected (HildonTouchSelector * selector,
2220
                                    gint column, GtkTreeIter * iter)
2221
{
2222
  GtkTreeSelection *selection = NULL;
2223
  HildonTouchSelectorColumn *current_column = NULL;
2224
  HildonTouchSelectorSelectionMode mode;
2225
  GtkTreeIter filter_iter;
2226
  gboolean result;
2227
2228
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
2229
  g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
2230
                        FALSE);
2231
  mode = hildon_touch_selector_get_column_selection_mode (selector);
2232
  g_return_val_if_fail
2233
    ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE) ||
2234
     ((mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)&&(column>0)),
2235
     FALSE);
2236
2237
  current_column = g_slist_nth_data (selector->priv->columns, column);
2238
2239
  selection =
2240
    gtk_tree_view_get_selection (GTK_TREE_VIEW (current_column->priv->tree_view));
2241
2242
  result = gtk_tree_selection_get_selected (selection, NULL, &filter_iter);
2243
2244
  if (iter) {
2245
    if (result == TRUE) {
2246
      gtk_tree_model_filter_convert_iter_to_child_iter
2247
        (GTK_TREE_MODEL_FILTER (current_column->priv->filter),
2248
         iter, &filter_iter);
2249
    } else {
2250
      memset (iter, 0, sizeof (GtkTreeIter));
2251
    }
2252
  }
2253
2254
  return result;
2255
}
2256
2257
/**
2258
 * hildon_touch_selector_select_iter:
2259
 * @selector: a #HildonTouchSelector
2260
 * @column:   the column to select an item from
2261
 * @iter:     the #GtkTreeIter to be selected
2262
 * @scroll_to: whether to smoothly scroll to the item
2263
 *
2264
 * Sets the currently selected item in the column @column to the one pointed by @iter,
2265
 * optionally smoothly scrolling to it.
2266
 *
2267
 * To obtain the currently selected iter, see hildon_touch_selector_get_selected()
2268
 *
2269
 * Since: 2.2
2270
 **/
2271
void
2272
hildon_touch_selector_select_iter (HildonTouchSelector * selector,
2273
                                   gint column, GtkTreeIter * iter,
2274
                                   gboolean scroll_to)
2275
{
2276
  GtkTreePath *filter_path;
2277
  GtkTreeIter filter_iter;
2278
  HildonTouchSelectorColumn *current_column = NULL;
2279
  GtkTreeView *tv = NULL;
2280
  GtkTreeSelection *selection = NULL;
2281
2282
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2283
  g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
2284
2285
  current_column = g_slist_nth_data (selector->priv->columns, column);
2286
2287
  tv = current_column->priv->tree_view;
2288
  selection = gtk_tree_view_get_selection (tv);
2289
2290
  /* The given iter might be not visible, due to the
2291
     GtkTreeModelFilter we use. If so, don't change the selection. */
2292
  if (gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (current_column->priv->filter),
2293
                                                        &filter_iter, iter) == FALSE)
2294
          return;
2295
2296
  filter_path = gtk_tree_model_get_path (current_column->priv->filter, &filter_iter);
2297
2298
  gtk_tree_selection_select_iter (selection, &filter_iter);
2299
2300
  if (scroll_to) {
2301
    hildon_touch_selector_scroll_to (current_column, tv, filter_path);
2302
  }
2303
2304
  hildon_touch_selector_emit_value_changed (selector, column);
2305
2306
  gtk_tree_path_free (filter_path);
2307
}
2308
2309
/**
2310
 * hildon_touch_selector_unselect_iter
2311
 * @selector: a #HildonTouchSelector
2312
 * @column:   the column to unselects from
2313
 * @iter:     the #GtkTreeIter to be unselected
2314
 *
2315
 * Unselect the item pointed by @iter in the column @column
2316
 *
2317
 * Since: 2.2
2318
 **/
2319
2320
void hildon_touch_selector_unselect_iter (HildonTouchSelector * selector,
2321
                                          gint column,
2322
                                          GtkTreeIter * iter)
2323
{
2324
  HildonTouchSelectorColumn *current_column = NULL;
2325
  GtkTreeSelection *selection = NULL;
2326
  GtkTreeIter filter_iter;
2327
2328
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2329
  g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
2330
2331
  current_column = g_slist_nth_data (selector->priv->columns, column);
2332
  selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
2333
  if (gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (current_column->priv->filter),
2334
                                                        &filter_iter, iter) == FALSE)
2335
    return;
2336
2337
  gtk_tree_selection_unselect_iter (selection, &filter_iter);
2338
  hildon_touch_selector_emit_value_changed (selector, column);
2339
}
2340
2341
/**
2342
 * hildon_touch_selector_unselect_all:
2343
 * @selector: a #HildonTouchSelector
2344
 * @column: the position of the column to get the selected rows from
2345
 *
2346
 * Unselects all the selected items in the column @column.
2347
 *
2348
 * Since: 2.2
2349
 **/
2350
void
2351
hildon_touch_selector_unselect_all (HildonTouchSelector * selector,
2352
                                    gint column)
2353
{
2354
  HildonTouchSelectorColumn *current_column = NULL;
2355
  GtkTreeSelection *selection = NULL;
2356
2357
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2358
  g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
2359
2360
  current_column = g_slist_nth_data (selector->priv->columns, column);
2361
  selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
2362
  gtk_tree_selection_unselect_all (selection);
2363
2364
  hildon_touch_selector_emit_value_changed (selector, column);
2365
}
2366
2367
/**
2368
 * hildon_touch_selector_filter_selected_to_child_selected:
2369
 * @filter: 
2370
 * @filter_selected: 
2371
 *
2372
 * Converts a list of #GtkTreePath<!-- -->s from the internal
2373
 * #GtkTreeModelFilter to the child #GtkTreeModel.
2374
 *
2375
 * Returns: a newly created list of #GtkTreePath<!-- -->s. The list
2376
 * and paths should be freed when not needed anymore.
2377
 **/
2378
static GList *
2379
hildon_touch_selector_filter_selected_to_child_selected (GtkTreeModelFilter *filter,
2380
                                                         GList *filter_selected)
2381
{
2382
    GList *iter;
2383
    GList *child_selected = NULL;
2384
2385
    for (iter = filter_selected; iter; iter = iter->next)
2386
        child_selected = g_list_append (child_selected,
2387
                                        gtk_tree_model_filter_convert_path_to_child_path (
2388
                                            filter, (GtkTreePath *)iter->data));
2389
2390
    return child_selected;
2391
}
2392
2393
/**
2394
 * hildon_touch_selector_get_selected_rows:
2395
 * @selector: a #HildonTouchSelector
2396
 * @column: the position of the column to get the selected rows from
2397
 *
2398
 * Creates a list of #GtkTreePath<!-- -->s of all selected rows in a column. Additionally,
2399
 * if you to plan to modify the model after calling this function, you may
2400
 * want to convert the returned list into a list of GtkTreeRowReferences. To do this,
2401
 * you can use gtk_tree_row_reference_new().
2402
 *
2403
 * See gtk_tree_selection_get_selected_rows() for more information.
2404
 *
2405
 * Returns: A new #GList containing a #GtkTreePath for each selected row in the column @column.
2406
 *
2407
 * Since: 2.2
2408
 **/
2409
GList *
2410
hildon_touch_selector_get_selected_rows (HildonTouchSelector * selector,
2411
                                         gint column)
2412
{
2413
  GList *result;
2414
  GList *filter_selected;
2415
  HildonTouchSelectorColumn *current_column = NULL;
2416
  GtkTreeSelection *selection = NULL;
2417
2418
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
2419
  g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
2420
                        NULL);
2421
2422
  current_column = g_slist_nth_data (selector->priv->columns, column);
2423
  selection = gtk_tree_view_get_selection (current_column->priv->tree_view);
2424
2425
  filter_selected = gtk_tree_selection_get_selected_rows (selection, NULL);
2426
  result = hildon_touch_selector_filter_selected_to_child_selected
2427
      (GTK_TREE_MODEL_FILTER (current_column->priv->filter),
2428
       filter_selected);
2429
  g_list_foreach (filter_selected, (GFunc) gtk_tree_path_free, NULL);
2430
  g_list_free (filter_selected);
2431
2432
  return result;
2433
}
2434
2435
/**
2436
 * hildon_touch_selector_get_model:
2437
 * @selector: a #HildonTouchSelector
2438
 * @column: the position of the column in @selector
2439
 *
2440
 * Gets the model of a column of @selector.
2441
 *
2442
 * Returns: the #GtkTreeModel for the column @column of @selector.
2443
 *
2444
 * Since: 2.2
2445
 **/
2446
GtkTreeModel *
2447
hildon_touch_selector_get_model (HildonTouchSelector * selector, gint column)
2448
{
2449
  HildonTouchSelectorColumn *current_column = NULL;
2450
2451
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
2452
  g_return_val_if_fail (column < hildon_touch_selector_get_num_columns (selector),
2453
                        NULL);
2454
2455
  current_column = g_slist_nth_data (selector->priv->columns, column);
2456
2457
  return current_column->priv->model;
2458
}
2459
2460
static void
2461
on_row_changed (GtkTreeModel *model,
2462
                GtkTreePath *path,
2463
                GtkTreeIter *iter,
2464
                gpointer userdata)
2465
{
2466
  HildonTouchSelector *selector;
2467
  HildonTouchSelectorColumn *current_column;
2468
  GtkTreePath *filter_path;
2469
2470
  gint column = 0;
2471
  GSList *col;
2472
2473
  selector = HILDON_TOUCH_SELECTOR (userdata);
2474
2475
  for (col = selector->priv->columns; col != NULL; col = col->next) {
2476
    current_column = HILDON_TOUCH_SELECTOR_COLUMN (col->data);
2477
    if (current_column->priv->model == model) {
2478
        filter_path =
2479
            gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (current_column->priv->filter),
2480
                                                              path);
2481
        if (filter_path &&
2482
            gtk_tree_selection_path_is_selected (gtk_tree_view_get_selection (current_column->priv->tree_view),
2483
                                                 filter_path)) {
2484
            hildon_touch_selector_emit_value_changed (selector, column);
2485
        }
2486
        gtk_tree_path_free (filter_path);
2487
    }
2488
    column ++;
2489
  }
2490
}
2491
2492
static void
2493
on_row_deleted (GtkTreeModel *model,
2494
                GtkTreePath *path,
2495
                gpointer userdata)
2496
{
2497
  HildonTouchSelector *selector = HILDON_TOUCH_SELECTOR (userdata);
2498
  gint column = 0;
2499
  GSList *col = selector->priv->columns;
2500
2501
  while (col != NULL) {
2502
    HildonTouchSelectorColumn *current_column;
2503
    current_column = HILDON_TOUCH_SELECTOR_COLUMN (col->data);
2504
    if (current_column->priv->model == model) {
2505
      GtkTreeSelection *sel = gtk_tree_view_get_selection (current_column->priv->tree_view);
2506
      if (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_BROWSE &&
2507
          gtk_tree_model_iter_n_children (model, NULL) > 0 &&
2508
          gtk_tree_selection_count_selected_rows (sel) == 0) {
2509
        GtkTreeIter iter;
2510
        GtkTreePath *first;
2511
2512
        gtk_tree_model_get_iter_first (current_column->priv->filter, &iter);
2513
        first = gtk_tree_model_get_path (current_column->priv->filter, &iter);
2514
        gtk_tree_selection_select_path (sel, first);
2515
        gtk_tree_path_free (first);
2516
      }
2517
      hildon_touch_selector_emit_value_changed (selector, column);
2518
    }
2519
    col = col->next;
2520
    column ++;
2521
  }
2522
}
2523
2524
static void
2525
_hildon_touch_selector_set_model (HildonTouchSelector * selector,
2526
                                  gint column, GtkTreeModel * model)
2527
{
2528
  HildonTouchSelectorColumn *current_column = NULL;
2529
2530
  current_column =
2531
    HILDON_TOUCH_SELECTOR_COLUMN (g_slist_nth_data (selector->priv->columns, column));
2532
2533
  if (current_column->priv->model) {
2534
    g_signal_handlers_disconnect_by_func (current_column->priv->model,
2535
                                          on_row_changed, selector);
2536
    g_signal_handlers_disconnect_by_func (current_column->priv->model,
2537
                                          on_row_deleted, selector);
2538
    g_object_unref (current_column->priv->model);
2539
  }
2540
2541
  current_column->priv->model = g_object_ref (model);
2542
2543
  if (current_column->priv->filter) {
2544
    g_object_unref (current_column->priv->filter);
2545
  }
2546
2547
  current_column->priv->filter = gtk_tree_model_filter_new (model, NULL);
2548
  gtk_tree_view_set_model (current_column->priv->tree_view,
2549
                           current_column->priv->filter);
2550
2551
  g_signal_connect (model, "row-changed",
2552
                    G_CALLBACK (on_row_changed), selector);
2553
  g_signal_connect_after (model, "row-deleted",
2554
                          G_CALLBACK (on_row_deleted), selector);
2555
}
2556
2557
/**
2558
 * hildon_touch_selector_set_model:
2559
 * @selector: a #HildonTouchSelector
2560
 * @column: the position of the column to set the model to
2561
 * @model: a #GtkTreeModel. It increases a reference so you are not
2562
 * going to use the model anymore, unref it.
2563
 *
2564
 * Sets the #GtkTreeModel for a particular column in @model.
2565
 *
2566
 * Note that this call adds a reference to @model, so you must unref
2567
 * it afterwards if you are not going to use it anymore.
2568
 *
2569
 * Since: 2.2
2570
 **/
2571
void
2572
hildon_touch_selector_set_model (HildonTouchSelector * selector,
2573
                                 gint column, GtkTreeModel * model)
2574
{
2575
  g_return_if_fail (HILDON_TOUCH_SELECTOR (selector));
2576
  g_return_if_fail (column < hildon_touch_selector_get_num_columns (selector));
2577
2578
  HILDON_TOUCH_SELECTOR_GET_CLASS (selector)->set_model (selector, column, model);
2579
}
2580
2581
/**
2582
 * hildon_touch_selector_get_current_text:
2583
 * @selector: a #HildonTouchSelector
2584
 *
2585
 * Returns a string representing the currently selected items for
2586
 * each column of @selector. See hildon_touch_selector_set_print_func().
2587
 *
2588
 * Returns: a newly allocated string.
2589
 *
2590
 * Since: 2.2
2591
 **/
2592
gchar *
2593
hildon_touch_selector_get_current_text (HildonTouchSelector * selector)
2594
{
2595
  gchar *result = NULL;
2596
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
2597
2598
  if (selector->priv->print_func) {
2599
    result = (*selector->priv->print_func) (selector, selector->priv->print_user_data);
2600
  } else {
2601
    result = _default_print_func (selector, NULL);
2602
  }
2603
2604
  return result;
2605
}
2606
2607
static void
2608
search_nearest_element (HildonPannableArea *panarea,
2609
                        GtkTreeView *tv,
2610
                        GList *selected_rows,
2611
                        GtkTreePath **nearest_path)
2612
{
2613
  GtkAdjustment *adj = NULL;
2614
  gdouble target_value = 0;
2615
  GdkRectangle rect;
2616
  GList *iter = NULL;
2617
  GtkTreePath *path = NULL;
2618
  gint y = -1;
2619
  gdouble nearest_distance = -1;
2620
  gdouble current_distance = -1;
2621
  GtkTreePath *result_path = NULL;
2622
2623
  g_assert (nearest_path != NULL);
2624
2625
  if (selected_rows == NULL) {
2626
    *nearest_path = NULL;
2627
    return;
2628
  }
2629
2630
  adj = hildon_pannable_area_get_vadjustment (panarea);
2631
  g_return_if_fail (adj != NULL);
2632
2633
  /* we add this in order to check the nearest to the center of
2634
     the visible area */
2635
  target_value = gtk_adjustment_get_value (adj) + adj->page_size/2;
2636
2637
  path = result_path = selected_rows->data;
2638
  gtk_tree_view_get_background_area (tv, path, NULL, &rect);
2639
  gtk_tree_view_convert_bin_window_to_tree_coords (tv, 0, rect.y, NULL, &y);
2640
  nearest_distance = abs (target_value - y);
2641
2642
  for (iter = selected_rows->next; iter; iter = g_list_next (iter)) {
2643
    gtk_tree_view_get_background_area (tv, path, NULL, &rect);
2644
    gtk_tree_view_convert_bin_window_to_tree_coords (tv, 0, rect.y, NULL, &y);
2645
    current_distance = abs (target_value - y);
2646
    if (current_distance < nearest_distance) {
2647
      nearest_distance = current_distance;
2648
      result_path = path;
2649
    }
2650
  }
2651
2652
  *nearest_path = result_path;
2653
}
2654
2655
static gboolean
2656
on_realize_cb                                  (GtkWidget *widget,
2657
                                                gpointer data)
2658
{
2659
  HildonTouchSelectorColumn *column = NULL;
2660
  GdkRectangle rect;
2661
  gint y;
2662
2663
  column = HILDON_TOUCH_SELECTOR_COLUMN (data);
2664
2665
  if (column->priv->initial_path) {
2666
    gtk_tree_view_get_background_area (GTK_TREE_VIEW (column->priv->tree_view),
2667
                                       column->priv->initial_path, NULL, &rect);
2668
    gtk_tree_view_convert_bin_window_to_tree_coords
2669
      (GTK_TREE_VIEW (column->priv->tree_view),
2670
       0, rect.y, NULL, &y);
2671
2672
    hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA (column->priv->panarea),
2673
                                    -1, y);
2674
2675
    gtk_tree_path_free (column->priv->initial_path);
2676
2677
    column->priv->initial_path = NULL;
2678
2679
  }
2680
2681
  g_signal_handler_disconnect (column->priv->panarea,
2682
                               column->priv->realize_handler);
2683
2684
  return FALSE;
2685
}
2686
2687
static void
2688
hildon_touch_selector_scroll_to (HildonTouchSelectorColumn *column,
2689
                                 GtkTreeView *tv,
2690
                                 GtkTreePath *path)
2691
{
2692
  if (GTK_WIDGET_REALIZED (column->priv->panarea)) {
2693
    GdkRectangle rect;
2694
    gint y;
2695
2696
    gtk_tree_view_get_background_area (tv,
2697
                                       path, NULL, &rect);
2698
    gtk_tree_view_convert_bin_window_to_tree_coords (tv,
2699
                                                     0, rect.y, NULL, &y);
2700
2701
    hildon_pannable_area_scroll_to (HILDON_PANNABLE_AREA
2702
                                    (column->priv->panarea), -1, y);
2703
  } else {
2704
    if (column->priv->realize_handler != 0) {
2705
2706
      if (column->priv->initial_path) {
2707
        gtk_tree_path_free (column->priv->initial_path);
2708
        column->priv->initial_path = NULL;
2709
      }
2710
2711
      g_signal_handler_disconnect (column->priv->panarea,
2712
                                   column->priv->realize_handler);
2713
      column->priv->realize_handler = 0;
2714
    }
2715
2716
    column->priv->initial_path = gtk_tree_path_copy (path);
2717
    column->priv->realize_handler =
2718
      g_signal_connect_after (G_OBJECT (column->priv->panarea), "realize",
2719
                              G_CALLBACK (on_realize_cb),
2720
                              column);
2721
  }
2722
}
2723
2724
/**
2725
 *
2726
 * Center on the selected item of a concrete column
2727
 *
2728
 * Returns: TRUE if was able to do that
2729
 *          FALSE otherwise
2730
 */
2731
static gboolean
2732
_hildon_touch_selector_center_on_selected_items (HildonTouchSelector *selector,
2733
                                                 HildonTouchSelectorColumn *column)
2734
{
2735
  GtkTreePath *path = NULL;
2736
  GList *selected_rows = NULL;
2737
  gint num_column = -1;
2738
2739
  num_column = g_slist_index (selector->priv->columns, column);
2740
2741
  selected_rows = hildon_touch_selector_get_selected_rows (selector, num_column);
2742
  if (selected_rows) {
2743
    search_nearest_element (HILDON_PANNABLE_AREA (column->priv->panarea),
2744
                             GTK_TREE_VIEW (column->priv->tree_view),
2745
                             selected_rows,
2746
                             &path);
2747
2748
    if (path != NULL) {
2749
      hildon_touch_selector_scroll_to (column,
2750
                                       GTK_TREE_VIEW (column->priv->tree_view),
2751
                                       path);
2752
    } else {
2753
      return FALSE;
2754
    }
2755
2756
    g_list_foreach (selected_rows, (GFunc) (gtk_tree_path_free), NULL);
2757
    g_list_free (selected_rows);
2758
  }
2759
2760
  return TRUE;
2761
}
2762
2763
static gboolean
2764
_hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
2765
{
2766
  HildonTouchSelectorSelectionMode mode;
2767
  gint n_columns;
2768
2769
  n_columns = hildon_touch_selector_get_num_columns (selector);
2770
  mode = hildon_touch_selector_get_column_selection_mode (selector);
2771
2772
  return ((n_columns > 1) || (mode == HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE));
2773
}
2774
2775
/**
2776
 * hildon_touch_selector_has_multiple_selection:
2777
 * @selector: A #HildonTouchSelector
2778
 *
2779
 * Determines whether @selector is complex enough to actually require an
2780
 * extra selection step than only picking an item. This is normally %TRUE
2781
 * if @selector has multiple columns, multiple selection, or when it is a
2782
 * more complex widget, like #HildonTouchSelectorEntry.
2783
 *
2784
 * This information is useful for widgets containing a #HildonTouchSelector,
2785
 * like #HildonPickerDialog, that could need a "Done" button, in case that
2786
 * its internal #HildonTouchSelector has multiple columns, for instance.
2787
 *
2788
 * Returns: %TRUE if @selector requires multiple selection steps.
2789
 *
2790
 * Since: 2.2
2791
 **/
2792
gboolean
2793
hildon_touch_selector_has_multiple_selection (HildonTouchSelector * selector)
2794
{
2795
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
2796
2797
  return HILDON_TOUCH_SELECTOR_GET_CLASS (selector)->has_multiple_selection (selector);
2798
}
2799
2800
2801
/**
2802
 * hildon_touch_selector_get_column:
2803
 * @selector: A #HildonTouchSelector
2804
 * @column: a column number
2805
 *
2806
 * Use this method to retrieve a #HildonTouchSelectorColumn. Then, you can use
2807
 * the #GtkCellLayout interface to set up the layout of the column.
2808
 *
2809
 * Returns: the @column<!-- -->-th #HildonTouchSelectorColumn in @selector
2810
 *
2811
 * Since: 2.2
2812
 **/
2813
HildonTouchSelectorColumn *
2814
hildon_touch_selector_get_column (HildonTouchSelector * selector,
2815
                                  gint column)
2816
{
2817
  gint num_columns = -1;
2818
2819
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), NULL);
2820
  num_columns = hildon_touch_selector_get_num_columns (selector);
2821
  g_return_val_if_fail (column < num_columns && column >= 0, NULL);
2822
2823
  return g_slist_nth_data (selector->priv->columns, column);
2824
}
2825
2826
2827
/**
2828
 * hildon_touch_selector_center_on_selected:
2829
 * @selector: a #HildonTouchSelector
2830
 *
2831
 * Ensures all the columns in a #HildonTouchSelector show a selected
2832
 * item. If one of the columns is in
2833
 * %HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE mode, that column
2834
 * will be scrolled to ensure the selected item that is closest to the
2835
 * currently visible area is shown.
2836
 *
2837
 * The #HildonTouchSelector:initial-scroll property configure the widget
2838
 * in order to use this function at the first show.
2839
 *
2840
 * Take into account that the element is not centered until the widget is
2841
 * realized. If the widget is not realized when the function is called, it
2842
 * will be postponed. If you call this functions several times before the
2843
 * widgets is realized, only the last one will be used.
2844
 *
2845
 * This behaviour includes any call to hildon_touch_selector_center_on_index(),
2846
 * so take care calling this functions, or with the
2847
 * #HildonTouchSelector:initial-scroll property in order to get centered on the
2848
 * proper element.
2849
 *
2850
 * Since: 2.2
2851
 **/
2852
void
2853
hildon_touch_selector_center_on_selected         (HildonTouchSelector *selector)
2854
{
2855
  GSList *iter = NULL;
2856
2857
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2858
2859
  for (iter = selector->priv->columns; iter; iter = g_slist_next (iter)) {
2860
    _hildon_touch_selector_center_on_selected_items (selector,
2861
                                                    HILDON_TOUCH_SELECTOR_COLUMN (iter->data));
2862
  }
2863
}
2864
2865
/**
2866
 * hildon_touch_selector_optimal_size_request
2867
 * @selector: a #HildonTouchSelector
2868
 * @requisition: a #GtkRequisition
2869
 *
2870
 * Gets the optimal size request of the touch selector. This function is mostly
2871
 * intended for dialog implementations that include a #HildonTouchSelector and
2872
 * want to optimize the screen real state, for example, when you want a dialog
2873
 * to show as much of the selector, avoiding any extra empty space below the
2874
 * selector.
2875
 *
2876
 * See #HildonPickerDialog implementation for an example.
2877
 *
2878
 * This function is oriented to be used in the size_request of a dialog or window,
2879
 * if you are not sure do not use it.
2880
 *
2881
 * There is a precondition to this function: Since this function does not
2882
 * call the "size_request" method, it can only be used when you know that
2883
 * gtk_widget_size_request() has been called since the last time a resize was
2884
 * queued.
2885
 *
2886
 * Since: 2.2
2887
 **/
2888
void
2889
hildon_touch_selector_optimal_size_request      (HildonTouchSelector *selector,
2890
                                                 GtkRequisition *requisition)
2891
{
2892
  GSList *iter = NULL;
2893
  gint height = 0;
2894
  gint base_height = 0;
2895
2896
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
2897
2898
  iter = selector->priv->columns;
2899
2900
  /* Default optimal values are the current ones */
2901
  gtk_widget_get_child_requisition (GTK_WIDGET (selector),
2902
                                    requisition);
2903
2904
  if (iter == NULL) {
2905
    height = requisition->height;
2906
  } else {
2907
    /* we use the normal requisition as base, as the touch selector can has
2908
       extra widgets, not only the columns (ie: HildonTouchSelectorEntry) */
2909
    base_height = requisition->height;
2910
  }
2911
2912
  /* Compute optimal height for the columns */
2913
  while (iter) {
2914
    HildonTouchSelectorColumn *column;
2915
    GtkWidget *child;
2916
    GtkRequisition child_requisition = {0};
2917
2918
    column = HILDON_TOUCH_SELECTOR_COLUMN (iter->data);
2919
    child = GTK_WIDGET (column->priv->tree_view);
2920
2921
    gtk_widget_get_child_requisition (child, &child_requisition);
2922
2923
    height = MAX (height, child_requisition.height);
2924
2925
    iter = g_slist_next (iter);
2926
  }
2927
2928
  requisition->height = base_height + height;
2929
}
2930
2931
2932
2933
/**
2934
 * hildon_touch_selector_get_hildon_ui_mode
2935
 * @selector: a #HildonTouchSelector
2936
 *
2937
 * Gets the current hildon-ui-mode, see #HildonUIMode for more information
2938
 *
2939
 * Returns: the current hildon-ui-mode
2940
 *
2941
 * Since: 2.2
2942
 **/
2943
HildonUIMode
2944
hildon_touch_selector_get_hildon_ui_mode        (HildonTouchSelector *selector)
2945
{
2946
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), HILDON_UI_MODE_EDIT);
2947
2948
  return selector->priv->hildon_ui_mode;
2949
}
2950
2951
/**
2952
 * hildon_touch_selector_set_hildon_ui_mode
2953
 * @selector: a #HildonTouchSelector
2954
 * @mode: a #HildonUIMode
2955
 *
2956
 * Sets the value of the property #HildonTouchSelector:hildon-ui-mode to be @mode,
2957
 * see #HildonUIMode for more information
2958
 *
2959
 * Note that the %HILDON_UI_MODE_NORMAL can be only used when the selector has
2960
 * one column, use the return value to check if the change was effective.
2961
 *
2962
 * Returns: %TRUE if #HildonTouchSelector:hildon-ui-mode was changed
2963
 *          %FALSE otherwise
2964
 *
2965
 * Since: 2.2
2966
 **/
2967
gboolean
2968
hildon_touch_selector_set_hildon_ui_mode        (HildonTouchSelector *selector,
2969
                                                 HildonUIMode         mode)
2970
{
2971
  gint num = 0;
2972
  GSList *iter = NULL;
2973
  HildonTouchSelectorColumn *column = NULL;
2974
  GtkTreeView *tree_view = NULL;
2975
2976
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
2977
  num = hildon_touch_selector_get_num_columns (selector);
2978
  g_return_val_if_fail ((mode == HILDON_UI_MODE_EDIT) || (num == 1), FALSE);
2979
2980
  if (mode == selector->priv->hildon_ui_mode) {
2981
    return FALSE;
2982
  }
2983
2984
  for (iter = selector->priv->columns; iter; iter = g_slist_next (iter)) {
2985
    column = HILDON_TOUCH_SELECTOR_COLUMN (iter->data);
2986
    tree_view = column->priv->tree_view;
2987
2988
    hildon_tree_view_set_hildon_ui_mode (tree_view, mode);
2989
2990
    /* looking at the code of hildon_tree_view_set_hildon_ui_mode, it seems
2991
       that it call the unselect_all, but it is required anyway */
2992
    if (mode == HILDON_UI_MODE_NORMAL) {
2993
      GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
2994
2995
      gtk_tree_selection_unselect_all (selection);
2996
    }
2997
  }
2998
2999
  selector->priv->hildon_ui_mode = mode;
3000
3001
  return TRUE;
3002
}
3003
3004
/**
3005
 * hildon_touch_selector_get_last_activated_row
3006
 * @selector: a #HildonTouchSelector
3007
 * @column: column number
3008
 *
3009
 * Gets a #GtkTreePath of the last row activated in a column (the last row that
3010
 * emitted a #GtkTreeView::row-activated signal). This is mainly useful if the
3011
 * @selector #HildonTouchSelector:hildon-ui-mode in set to %HILDON_UI_MODE_NORMAL,
3012
 * as using this state there is no real selection, so a method like
3013
 * hildon_touch_selector_get_selected_rows() will return always a empty
3014
 * selection.
3015
 *
3016
 * Anyway, this method works as well on %HILDON_UI_MODE_EDIT, but in this case
3017
 * is better, and more useful, to get the current selection.
3018
 *
3019
 * Returns: a newly allocated #GtkTreePath pointing to the last activated row
3020
 *          NULL if no row were activated.
3021
 *
3022
 * Since: 2.2
3023
 **/
3024
GtkTreePath*
3025
hildon_touch_selector_get_last_activated_row    (HildonTouchSelector *selector,
3026
                                                 gint                 column)
3027
{
3028
  HildonTouchSelectorColumn *selector_column = NULL;
3029
3030
  /* this method with check selector and that the column number is correct*/
3031
  selector_column = hildon_touch_selector_get_column (selector, column);
3032
3033
  if (selector_column == NULL) {
3034
    return NULL;
3035
  }
3036
3037
  if (selector_column->priv->last_activated != NULL) {
3038
    return gtk_tree_row_reference_get_path (selector_column->priv->last_activated);
3039
  } else {
3040
    return NULL;
3041
  }
3042
}
3043
3044
3045
/**
3046
 * hildon_touch_selector_center_on_index:
3047
 * @selector: a #HildonTouchSelector
3048
 * @column: column number
3049
 * @index: the index of the item to center on
3050
 *
3051
 * Ensures that the column number @column shows the element @index
3052
 *
3053
 * This is similar to hildon_touch_selector_center_on_selected() but with the
3054
 * difference that allows to center on a column item not selected.
3055
 *
3056
 * Take into account that the element is not centered until the widget is
3057
 * realized. If the widget is not realized when the function is called, it will
3058
 * be postponed. If you call this function several times before the widget is
3059
 * realized, only the last one will be used.
3060
 *
3061
 * This behaviour includes any call to hildon_touch_selector_center_on_selected().
3062
 * Check this function for more tips.
3063
 *
3064
 * Since: 2.2
3065
 **/
3066
void
3067
hildon_touch_selector_center_on_index (HildonTouchSelector *selector,
3068
                                       gint column,
3069
                                       gint index)
3070
{
3071
  HildonTouchSelectorColumn *current_column = NULL;
3072
  GtkTreePath *path = NULL;
3073
3074
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
3075
  g_return_if_fail ((column >= 0) && (column < hildon_touch_selector_get_num_columns (selector)));
3076
  g_return_if_fail (index >= 0);
3077
3078
  current_column = g_slist_nth_data (selector->priv->columns, column);
3079
3080
  path = gtk_tree_path_new_from_indices (index, -1);
3081
3082
  hildon_touch_selector_scroll_to (current_column,
3083
                                   current_column->priv->tree_view,
3084
                                   path);
3085
  gtk_tree_path_free (path);
3086
}
3087
3088
/**
3089
 * hildon_touch_selector_set_live_search:
3090
 * @selector: a #HildonTouchSelector
3091
 * @live_search: whether @selector should have live search capabilities
3092
 *
3093
 * Toggles a #HildonLiveSearch widget in @selector. This is enabled by
3094
 * default in #HildonTouchSelector but disabled for some of its
3095
 * subclasses like #HildonTouchSelectorEntry, #HildonDateSelector, and
3096
 * #HildonTimeSelector.  For the former, #HildonLiveSearch makes no
3097
 * sense. In a multi-column #HildonTouchSelector, #HildonLiveSearch
3098
 * is not supported, so don't try to enable it in one.
3099
 *
3100
 * If more columns are added to @selector, the #HildonLiveSearch
3101
 * will be removed and the property set to %FALSE.
3102
 *
3103
 * Since: 2.2.10
3104
 **/
3105
void
3106
hildon_touch_selector_set_live_search (HildonTouchSelector *selector,
3107
                                       gboolean live_search)
3108
{
3109
  HildonTouchSelectorColumn *col;
3110
3111
  g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (selector));
3112
3113
  if (selector->priv->has_live_search == live_search)
3114
    return;
3115
3116
  if (live_search) {
3117
    if (selector->priv->columns &&
3118
        selector->priv->columns->next == NULL) {
3119
      /* There is one and only one column already.  */
3120
      col = (HildonTouchSelectorColumn *) selector->priv->columns->data;
3121
      /* There is already a livesearch widget. Let's hook it up.  */
3122
      if (col->priv->livesearch) {
3123
        hildon_live_search_widget_hook (HILDON_LIVE_SEARCH (col->priv->livesearch),
3124
                                        GTK_WIDGET (col->priv->vbox),
3125
                                        GTK_WIDGET (col->priv->tree_view));
3126
      } else {
3127
        /* There is no livesearch widget yet. Create one.  */
3128
        hildon_touch_selector_add_live_search (selector, col);
3129
      }
3130
    } else if (selector->priv->columns &&
3131
               selector->priv->columns->next != NULL) {
3132
      g_critical ("Trying to set HildonTouchSelector::live-search to TRUE "
3133
                  "in a HildonTouchSelector instance with more than one column.");
3134
      return;
3135
    }
3136
  } else {
3137
    if (selector->priv->columns &&
3138
        selector->priv->columns->next == NULL) {
3139
        col = (HildonTouchSelectorColumn *) selector->priv->columns->data;
3140
        gtk_widget_hide (col->priv->livesearch);
3141
        hildon_live_search_widget_unhook (HILDON_LIVE_SEARCH (col->priv->livesearch));
3142
    }
3143
  }
3144
3145
  selector->priv->has_live_search = live_search;
3146
}
3147
3148
/**
3149
 * hildon_touch_selector_get_live_search:
3150
 * @selector: a #HildonTouchSelector
3151
 *
3152
 * Whether @selector has an embedded #HildonLiveSearch widget
3153
 *
3154
 * Returns: %TRUE if there is a #HildonLiveSearch in @selector, %FALSE
3155
 * otherwise
3156
 *
3157
 * Since: 2.2.10
3158
 **/
3159
gboolean
3160
hildon_touch_selector_get_live_search (HildonTouchSelector *selector)
3161
{
3162
  g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
3163
3164
  return selector->priv->has_live_search;
3165
}