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