Make sure that all timeouts in HildonBanner are removed
[hildon] / hildon / hildon-picker-button.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2008 Nokia Corporation, all rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser Public License as published by
8  * the Free Software Foundation; version 2 of the license.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser Public License for more details.
14  *
15  */
16
17 /**
18  * SECTION:hildon-picker-button
19  * @short_description: A button that launches a #HildonPickerDialog and displays the
20  * selected item
21  * @see_also: #HildonTouchSelector, #HildonPickerDialog
22  *
23  * #HildonPickerButton is a widget that lets the user select a particular item from
24  * a list. Visually, it's a button with title and value labels that brings up a
25  * #HildonPickerDialog. The user can then use this dialog to choose an item, which
26  * will be displayed in the value label of the button. The title of the dialog is
27  * taken from the button's main label (see hildon_button_get_title()).
28  *
29  * You should create your own #HildonTouchSelector at convenience and set it
30  * to the #HildonPickerButton with hildon_picker_button_set_selector(). For
31  * the common use cases of buttons to select date and time, you can use #HildonDateButton
32  * and #HildonTimeButton.
33  *
34  * <example>
35  * <programlisting>
36  * GtkWidget *
37  * create_selector (void)
38  * {
39  *   GtkWidget *selector;
40  * <!-- -->
41  *   selector = hildon_touch_selector_new_text ();
42  * <!-- -->
43  *   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), "America");
44  *   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), "Europe");
45  *   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), "Asia");
46  *   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), "Africa");
47  *   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), "Australia");
48  * <!-- -->
49  *   hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, 2);
50  * <!-- -->
51  *   return selector;
52  * }
53  * <!-- -->
54  * GtkWidget *
55  * create_button (HildonTouchSelector *selector)
56  * {
57  *   GtkWidget *button;
58  * <!-- -->
59  *   button = hildon_picker_button_new (HILDON_SIZE_AUTO, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
60  *   hildon_button_set_title (HILDON_BUTTON (button), "Continent");
61  * <!-- -->
62  *   hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (button),
63  *                                      HILDON_TOUCH_SELECTOR (selector));
64  * <!-- -->
65  *   return button;
66  * }
67  * </programlisting>
68  * </example>
69  */
70
71 #include "hildon-picker-button.h"
72 #include "hildon-picker-button-private.h"
73 #include "hildon-picker-dialog.h"
74
75 G_DEFINE_TYPE (HildonPickerButton, hildon_picker_button, HILDON_TYPE_BUTTON)
76
77 #define GET_PRIVATE(o)                                                  \
78   (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PICKER_BUTTON, HildonPickerButtonPrivate))
79
80 typedef struct _HildonPickerButtonPrivate HildonPickerButtonPrivate;
81
82 struct _HildonPickerButtonPrivate
83 {
84   GtkWidget *selector;
85   GtkWidget *dialog;
86   gchar *done_button_text;
87   guint disable_value_changed : 1;
88 };
89
90 /* Signals */
91 enum
92 {
93   VALUE_CHANGED,
94   LAST_SIGNAL
95 };
96
97 enum
98 {
99   PROP_SELECTOR = 1,
100   PROP_DONE_BUTTON_TEXT
101 };
102
103 static guint picker_button_signals[LAST_SIGNAL] = { 0 };
104
105 static gboolean
106 _current_selector_empty                         (HildonPickerButton *button);
107
108 static void
109 hildon_picker_button_selector_selection_changed (HildonTouchSelector * selector,
110                                                  gint column,
111                                                  gpointer user_data);
112
113 static void
114 hildon_picker_button_selector_columns_changed   (HildonTouchSelector * selector,
115                                                  gpointer user_data);
116
117
118 static void
119 hildon_picker_button_get_property (GObject * object, guint property_id,
120                                    GValue * value, GParamSpec * pspec)
121 {
122   switch (property_id) {
123   case PROP_SELECTOR:
124     g_value_set_object (value,
125                         hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (object)));
126     break;
127   case PROP_DONE_BUTTON_TEXT:
128     g_value_set_string (value,
129                         hildon_picker_button_get_done_button_text (HILDON_PICKER_BUTTON (object)));
130     break;
131   default:
132     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
133   }
134 }
135
136 static void
137 hildon_picker_button_set_property (GObject * object, guint property_id,
138                                    const GValue * value, GParamSpec * pspec)
139 {
140   switch (property_id) {
141   case PROP_SELECTOR:
142     hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (object),
143                                        g_value_get_object (value));
144     break;
145   case PROP_DONE_BUTTON_TEXT:
146     hildon_picker_button_set_done_button_text (HILDON_PICKER_BUTTON (object),
147                                                g_value_get_string (value));
148     break;
149   default:
150     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
151   }
152 }
153
154 static void
155 hildon_picker_button_finalize (GObject * object)
156 {
157   HildonPickerButtonPrivate *priv;
158
159   priv = GET_PRIVATE (object);
160
161   if (priv->selector) {
162     g_signal_handlers_disconnect_by_func (priv->selector,
163                                           hildon_picker_button_selector_selection_changed,
164                                           object);
165     g_signal_handlers_disconnect_by_func (priv->selector,
166                                           hildon_picker_button_selector_columns_changed,
167                                           object);
168     g_object_unref (priv->selector);
169     priv->selector = NULL;
170   }
171   if (priv->dialog) {
172     gtk_widget_destroy (priv->dialog);
173     priv->dialog = NULL;
174   }
175
176   if (priv->done_button_text) {
177     g_free (priv->done_button_text);
178     priv->done_button_text = NULL;
179   }
180
181   G_OBJECT_CLASS (hildon_picker_button_parent_class)->finalize (object);
182 }
183
184 /**
185  * hildon_picker_button_value_changed:
186  * @button: a #HildonPickerButton
187  *
188  * Emits a "#HildonPickerButton::value-changed" signal to the given
189  * #HildonPickerButton
190  *
191  * Since: 2.2
192  **/
193 void
194 hildon_picker_button_value_changed              (HildonPickerButton *button)
195 {
196   HildonPickerButtonPrivate *priv;
197   g_return_if_fail (HILDON_IS_PICKER_BUTTON (button));
198   priv = GET_PRIVATE (button);
199
200   if (!priv->disable_value_changed)
201     g_signal_emit (button, picker_button_signals[VALUE_CHANGED], 0);
202 }
203
204 G_GNUC_INTERNAL void
205 hildon_picker_button_disable_value_changed      (HildonPickerButton *button,
206                                                  gboolean            disable)
207 {
208   HildonPickerButtonPrivate *priv;
209   g_return_if_fail (HILDON_IS_PICKER_BUTTON (button));
210   priv = GET_PRIVATE (button);
211
212   priv->disable_value_changed = disable;
213 }
214
215 static void
216 _selection_changed (HildonPickerButton *button)
217 {
218   HildonPickerButtonPrivate *priv = GET_PRIVATE (button);
219
220   if (!GTK_IS_WINDOW (priv->dialog) ||
221       !GTK_WIDGET_VISIBLE (GTK_WINDOW (priv->dialog))) {
222     gchar *value = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (priv->selector));
223     if (value) {
224       hildon_button_set_value (HILDON_BUTTON (button), value);
225       g_free (value);
226       hildon_picker_button_value_changed (button);
227     }
228   }
229 }
230
231 static void
232 hildon_picker_button_on_dialog_response (GtkDialog *dialog,
233                                          gint       response,
234                                          gpointer   user_data)
235 {
236   HildonPickerButton *button;
237   HildonPickerButtonPrivate *priv;
238   gchar *value;
239
240   button = HILDON_PICKER_BUTTON (user_data);
241   priv = GET_PRIVATE (button);
242
243   if (response == GTK_RESPONSE_OK) {
244     value = hildon_touch_selector_get_current_text
245             (HILDON_TOUCH_SELECTOR (priv->selector));
246     hildon_button_set_value (HILDON_BUTTON (button), value);
247     g_free (value);
248     hildon_picker_button_value_changed (button);
249   }
250
251   gtk_widget_hide (GTK_WIDGET (dialog));
252 }
253
254 static void
255 hildon_picker_button_clicked (GtkButton * button)
256 {
257   GtkWidget *parent;
258   HildonPickerButtonPrivate *priv;
259
260   priv = GET_PRIVATE (HILDON_PICKER_BUTTON (button));
261
262   g_return_if_fail (HILDON_IS_TOUCH_SELECTOR (priv->selector));
263
264   /* Create the dialog if it doesn't exist already.  */
265   if (!priv->dialog) {
266     parent = gtk_widget_get_toplevel (GTK_WIDGET (button));
267     if (GTK_WIDGET_TOPLEVEL (parent)) {
268       priv->dialog = hildon_picker_dialog_new (GTK_WINDOW (parent));
269     } else {
270       priv->dialog = hildon_picker_dialog_new (NULL);
271     }
272
273     hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (priv->dialog),
274                                        HILDON_TOUCH_SELECTOR (priv->selector));
275     if (priv->done_button_text) {
276       hildon_picker_dialog_set_done_label (HILDON_PICKER_DIALOG (priv->dialog),
277                                            priv->done_button_text);
278     }
279
280     gtk_window_set_modal (GTK_WINDOW (priv->dialog),
281                           gtk_window_get_modal (GTK_WINDOW (parent)));
282     gtk_window_set_title (GTK_WINDOW (priv->dialog),
283                           hildon_button_get_title (HILDON_BUTTON (button)));
284     g_signal_connect (priv->dialog, "response",
285                       G_CALLBACK (hildon_picker_button_on_dialog_response),
286                       button);
287     g_signal_connect (priv->dialog, "delete-event",
288                       G_CALLBACK (gtk_widget_hide_on_delete),
289                       NULL);
290   }
291
292   if (_current_selector_empty (HILDON_PICKER_BUTTON (button))) {
293     g_warning ("There are no elements in the selector. Nothing to show.");
294   } {
295     gtk_window_present (GTK_WINDOW (priv->dialog));
296   }
297 }
298
299 static void
300 hildon_picker_button_selector_selection_changed (HildonTouchSelector * selector,
301                                                  gint column,
302                                                  gpointer user_data)
303
304 {
305   _selection_changed (HILDON_PICKER_BUTTON (user_data));
306 }
307
308 static void
309 hildon_picker_button_selector_columns_changed (HildonTouchSelector * selector,
310                                                gpointer user_data)
311
312 {
313   _selection_changed (HILDON_PICKER_BUTTON (user_data));
314 }
315
316 static void
317 hildon_picker_button_class_init (HildonPickerButtonClass * klass)
318 {
319   GObjectClass *object_class = G_OBJECT_CLASS (klass);
320   GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
321
322   g_type_class_add_private (klass, sizeof (HildonPickerButtonPrivate));
323
324   object_class->get_property = hildon_picker_button_get_property;
325   object_class->set_property = hildon_picker_button_set_property;
326   object_class->finalize = hildon_picker_button_finalize;
327
328   button_class->clicked = hildon_picker_button_clicked;
329
330   g_object_class_install_property (object_class,
331                                    PROP_SELECTOR,
332                                    g_param_spec_object ("touch-selector",
333                                                         "HildonTouchSelector widget",
334                                                         "HildonTouchSelector widget to be launched on button clicked",
335                                                         HILDON_TYPE_TOUCH_SELECTOR,
336                                                         G_PARAM_READWRITE));
337   g_object_class_install_property (object_class,
338                                    PROP_DONE_BUTTON_TEXT,
339                                    g_param_spec_string ("done-button-text",
340                                                         "HildonPickerDialog \"done\" button text",
341                                                         "The text for the \"done\" button in the dialog launched",
342                                                         NULL,
343                                                         G_PARAM_READWRITE));
344
345   /**
346    * HildonPickerButton::value-changed:
347    * @widget: the widget that received the signal
348    *
349    * The ::value-changed signal is emitted each time the user chooses a different
350    * item from the #HildonTouchSelector related, and the value label gets updated.
351    *
352    * Since: 2.2
353    */
354   picker_button_signals[VALUE_CHANGED] =
355     g_signal_new ("value-changed",
356                   G_TYPE_FROM_CLASS (klass),
357                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
358                   0,
359                   NULL, NULL,
360                   g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
361 }
362
363 static void
364 hildon_picker_button_init (HildonPickerButton * self)
365 {
366   HildonPickerButtonPrivate *priv;
367
368   priv = GET_PRIVATE (self);
369
370   priv->dialog = NULL;
371   priv->selector = NULL;
372   priv->done_button_text = NULL;
373   priv->disable_value_changed = FALSE;
374
375   hildon_button_set_style (HILDON_BUTTON (self),
376                            HILDON_BUTTON_STYLE_PICKER);
377 }
378
379 static gboolean
380 _current_selector_empty (HildonPickerButton *button)
381 {
382   HildonPickerButtonPrivate *priv;
383   HildonTouchSelector *selector = NULL;
384   GtkTreeModel *model = NULL;
385   GtkTreeIter iter;
386   gint i = 0;
387
388   priv = GET_PRIVATE (button);
389   selector = HILDON_TOUCH_SELECTOR (priv->selector);
390
391   g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), TRUE);
392
393   if (hildon_touch_selector_has_multiple_selection (selector)) {
394     return FALSE;
395   } else {
396     for (i=0; i < hildon_touch_selector_get_num_columns (selector); i++) {
397       model = hildon_touch_selector_get_model (selector, i);
398
399       if (gtk_tree_model_get_iter_first (model, &iter)) {
400         return FALSE;
401       }
402     }
403     return TRUE;
404   }
405 }
406
407 /**
408  * hildon_picker_button_new:
409  * @size: One of #HildonSizeType, specifying the size of the new button.
410  * @arrangement: one of #HildonButtonArrangement, specifying the placement of the
411  * labels.
412  *
413  * Creates a new #HildonPickerButton. See hildon_button_new() for details on the
414  * parameters.
415  *
416  * Returns: a newly created #HildonPickerButton
417  *
418  * Since: 2.2
419  **/
420 GtkWidget *
421 hildon_picker_button_new (HildonSizeType          size,
422                           HildonButtonArrangement arrangement)
423 {
424   GtkWidget *button;
425
426   button = g_object_new (HILDON_TYPE_PICKER_BUTTON,
427                          "arrangement", arrangement, "size", size,
428                          NULL);
429
430   return button;
431 }
432
433 /**
434  * hildon_picker_button_set_selector:
435  * @button: a #HildonPickerButton
436  * @selector: a #HildonTouchSelector
437  *
438  * Sets @selector as the #HildonTouchSelector to be shown in the
439  * #HildonPickerDialog that @button brings up.
440  *
441  * Since: 2.2
442  **/
443 void
444 hildon_picker_button_set_selector (HildonPickerButton * button,
445                                    HildonTouchSelector * selector)
446 {
447   HildonPickerButtonPrivate *priv;
448   gchar *value = NULL;
449
450   g_return_if_fail (HILDON_IS_PICKER_BUTTON (button));
451   g_return_if_fail (!selector || HILDON_IS_TOUCH_SELECTOR (selector));
452
453   priv = GET_PRIVATE (button);
454
455   if (priv->selector) {
456     g_signal_handlers_disconnect_by_func (priv->selector,
457                                           hildon_picker_button_selector_selection_changed,
458                                           button);
459     g_signal_handlers_disconnect_by_func (priv->selector,
460                                           hildon_picker_button_selector_columns_changed,
461                                           button);
462     g_object_unref (priv->selector);
463   }
464
465   priv->selector = GTK_WIDGET (selector);
466
467   if (selector) {
468     g_object_ref (selector);
469
470     g_signal_connect (G_OBJECT (selector), "changed",
471                       G_CALLBACK (hildon_picker_button_selector_selection_changed),
472                       button);
473
474     g_signal_connect (G_OBJECT (selector), "columns-changed",
475                       G_CALLBACK (hildon_picker_button_selector_columns_changed),
476                       button);
477
478     value = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (priv->selector));
479   }
480
481   if (!value)
482     value = g_strdup ("");
483
484   hildon_button_set_value (HILDON_BUTTON (button), value);
485   hildon_picker_button_value_changed (button);
486
487   g_free (value);
488 }
489
490 /**
491  * hildon_picker_button_get_selector:
492  * @button: a #HildonPickerButton
493  *
494  * Retrieves the #HildonTouchSelector associated to @button.
495  *
496  * Returns: a #HildonTouchSelector
497  *
498  * Since: 2.2
499  **/
500 HildonTouchSelector *
501 hildon_picker_button_get_selector (HildonPickerButton * button)
502 {
503   HildonPickerButtonPrivate *priv;
504
505   g_return_val_if_fail (HILDON_IS_PICKER_BUTTON (button), NULL);
506
507   priv = GET_PRIVATE (button);
508
509   return HILDON_TOUCH_SELECTOR (priv->selector);
510 }
511
512 /**
513  * hildon_picker_button_get_active:
514  * @button: a #HildonPickerButton
515  *
516  * Returns the index of the currently active item, or -1 if there's no
517  * active item. If the selector has several columns, only the first
518  * one is used.
519  *
520  * Returns: an integer which is the index of the currently active item, or -1 if there's no active item.
521  *
522  * Since: 2.2
523  **/
524 gint
525 hildon_picker_button_get_active                 (HildonPickerButton * button)
526 {
527   HildonTouchSelector *sel;
528   g_return_val_if_fail (HILDON_IS_PICKER_BUTTON (button), -1);
529
530   sel = hildon_picker_button_get_selector (button);
531
532   return hildon_touch_selector_get_active (sel, 0);
533 }
534
535 /**
536  * hildon_picker_button_set_active:
537  * @button: a #HildonPickerButton
538  * @index: the index of the item to select, or -1 to have no active item
539  *
540  * Sets the active item of the #HildonTouchSelector associated to
541  * @button to @index. If the selector has several columns, only the
542  * first one is used.
543  *
544  * Since: 2.2
545  **/
546 void
547 hildon_picker_button_set_active                 (HildonPickerButton * button,
548                                                  gint index)
549 {
550   HildonTouchSelector *sel;
551   gchar *text;
552   g_return_if_fail (HILDON_IS_PICKER_BUTTON (button));
553
554   sel = hildon_picker_button_get_selector (button);
555   hildon_touch_selector_set_active (sel, 0, index);
556
557   text = hildon_touch_selector_get_current_text (sel);
558   hildon_button_set_value (HILDON_BUTTON (button), text);
559   g_free (text);
560 }
561
562 /**
563  * hildon_picker_button_get_done_button_text:
564  * @button: a #HildonPickerButton
565  *
566  * Gets the text used in the #HildonPickerDialog that is launched by
567  * @button. If no custom text is set, then %NULL is returned.
568  *
569  * Returns: the custom string to be used, or %NULL if the default
570  * #HildonPickerDialog::done-button-text is to be used.
571  *
572  * Since: 2.2
573  **/
574 const gchar *
575 hildon_picker_button_get_done_button_text (HildonPickerButton *button)
576 {
577   HildonPickerButtonPrivate *priv;
578
579   g_return_val_if_fail (HILDON_IS_PICKER_BUTTON (button), NULL);
580
581   priv = GET_PRIVATE (button);
582
583   return priv->done_button_text;
584 }
585
586 /**
587  * hildon_picker_button_set_done_button_text:
588  * @button: a #HildonPickerButton
589  * @done_button_text: a string
590  *
591  * Sets a custom string to be used in the "done" button in #HildonPickerDialog.
592  * If unset, the default HildonPickerButton::done-button-text property
593  * value will be used.
594  *
595  * Since: 2.2
596  **/
597 void
598 hildon_picker_button_set_done_button_text (HildonPickerButton *button,
599                                            const gchar *done_button_text)
600 {
601   HildonPickerButtonPrivate *priv;
602
603   g_return_if_fail (HILDON_IS_PICKER_BUTTON (button));
604   g_return_if_fail (done_button_text != NULL);
605
606   priv = GET_PRIVATE (button);
607
608   g_free (priv->done_button_text);
609   priv->done_button_text = g_strdup (done_button_text);
610
611   if (priv->dialog) {
612     hildon_picker_dialog_set_done_label (HILDON_PICKER_DIALOG (priv->dialog),
613                                          priv->done_button_text);
614   }
615 }