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