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