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