b8cf0a56fe5f6a33f8afabc4499101286dbffdaa
[hildon] / src / hildon-button.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2008 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Karl Lattimer <karl.lattimer@nokia.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser Public License as published by
10  * the Free Software Foundation; version 2 of the license.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser Public License for more details.
16  *
17  */
18
19 /**
20  * SECTION:hildon-button
21  * @short_description: Widget representing a button in the Hildon framework.
22  *
23  * The #HildonButton is a GTK widget which represents a clickable
24  * button. It is derived from the GtkButton widget and provides
25  * additional commodities specific to the Hildon framework.
26  *
27  * The height of a #HildonButton can be set to either "finger" height
28  * or "thumb" height. It can also be configured to use halfscreen or
29  * fullscreen width. Alternatively, either dimension can be set to
30  * "auto" so it behaves like a standard GtkButton.
31  *
32  * The #HildonButton can hold any valid child widget, but it usually
33  * contains two labels, named title and value. To change the alignment
34  * of the labels, use gtk_button_set_alignment()
35  */
36
37 #include                                        "hildon-button.h"
38 #include                                        "hildon-enum-types.h"
39 #include                                        "hildon-gtk.h"
40
41 G_DEFINE_TYPE                                   (HildonButton, hildon_button, GTK_TYPE_BUTTON);
42
43 #define                                         HILDON_BUTTON_GET_PRIVATE(obj) \
44                                                 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
45                                                 HILDON_TYPE_BUTTON, HildonButtonPrivate));
46
47 typedef struct                                  _HildonButtonPrivate HildonButtonPrivate;
48
49 struct                                          _HildonButtonPrivate
50 {
51     GtkLabel *title;
52     GtkLabel *value;
53     GtkBox *hbox;
54     GtkWidget *label_box;
55     GtkWidget *alignment;
56     GtkWidget *image;
57     GtkPositionType image_position;
58 };
59
60 enum {
61     PROP_TITLE = 1,
62     PROP_VALUE,
63     PROP_SIZE,
64     PROP_ARRANGEMENT
65 };
66
67 static void
68 hildon_button_set_arrangement                   (HildonButton            *button,
69                                                  HildonButtonArrangement  arrangement);
70
71 static void
72 hildon_button_construct_child                   (HildonButton *button);
73
74 static void
75 hildon_button_set_property                      (GObject      *object,
76                                                  guint         prop_id,
77                                                  const GValue *value,
78                                                  GParamSpec   *pspec)
79 {
80     HildonButton *button = HILDON_BUTTON (object);
81
82     switch (prop_id)
83     {
84     case PROP_TITLE:
85         hildon_button_set_title (button, g_value_get_string (value));
86         break;
87     case PROP_VALUE:
88         hildon_button_set_value (button, g_value_get_string (value));
89         break;
90     case PROP_SIZE:
91         hildon_gtk_widget_set_theme_size (GTK_WIDGET (button), g_value_get_flags (value));
92         break;
93     case PROP_ARRANGEMENT:
94         hildon_button_set_arrangement (button, g_value_get_enum (value));
95         break;
96     default:
97         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
98         break;
99     }
100 }
101
102 static void
103 hildon_button_get_property                      (GObject    *object,
104                                                  guint       prop_id,
105                                                  GValue     *value,
106                                                  GParamSpec *pspec)
107 {
108     HildonButton *button = HILDON_BUTTON (object);
109     HildonButtonPrivate *priv = HILDON_BUTTON_GET_PRIVATE (button);
110
111     switch (prop_id)
112     {
113     case PROP_TITLE:
114         g_value_set_string (value, gtk_label_get_text (priv->title));
115         break;
116     case PROP_VALUE:
117         g_value_set_string (value, gtk_label_get_text (priv->value));
118         break;
119     default:
120         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
121         break;
122     }
123 }
124
125 static void
126 hildon_button_class_init                        (HildonButtonClass *klass)
127 {
128     GObjectClass *gobject_class = (GObjectClass *)klass;
129     GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
130
131     gobject_class->set_property = hildon_button_set_property;
132     gobject_class->get_property = hildon_button_get_property;
133
134     g_object_class_install_property (
135         gobject_class,
136         PROP_TITLE,
137         g_param_spec_string (
138             "title",
139             "Title",
140             "Text of the title label inside the button",
141             NULL,
142             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
143
144     g_object_class_install_property (
145         gobject_class,
146         PROP_VALUE,
147         g_param_spec_string (
148             "value",
149             "Value",
150             "Text of the value label inside the button",
151             NULL,
152             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
153
154     g_object_class_install_property (
155         gobject_class,
156         PROP_SIZE,
157         g_param_spec_flags (
158             "size",
159             "Size",
160             "Size request for the button",
161             HILDON_TYPE_SIZE_TYPE,
162             HILDON_SIZE_AUTO,
163             G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
164
165     g_object_class_install_property (
166         gobject_class,
167         PROP_ARRANGEMENT,
168         g_param_spec_enum (
169             "arrangement",
170             "Arrangement",
171             "How the button contents must be arranged",
172             HILDON_TYPE_BUTTON_ARRANGEMENT,
173             HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
174             G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
175
176     gtk_widget_class_install_style_property (
177         widget_class,
178         g_param_spec_uint (
179             "horizontal-spacing",
180             "Horizontal spacing between labels",
181             "Horizontal spacing between the title and value labels, when in horizontal mode",
182             0, G_MAXUINT, 25,
183             G_PARAM_READABLE));
184
185     gtk_widget_class_install_style_property (
186         widget_class,
187         g_param_spec_uint (
188             "vertical-spacing",
189             "Vertical spacing between labels",
190             "Vertical spacing between the title and value labels, when in vertical mode",
191             0, G_MAXUINT, 5,
192             G_PARAM_READABLE));
193
194     g_type_class_add_private (klass, sizeof (HildonButtonPrivate));
195 }
196
197 static void
198 hildon_button_init                              (HildonButton *self)
199 {
200     HildonButtonPrivate *priv = HILDON_BUTTON_GET_PRIVATE (self);
201
202     priv->title = GTK_LABEL (gtk_label_new (NULL));
203     priv->value = GTK_LABEL (gtk_label_new (NULL));
204     priv->alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
205     priv->image = NULL;
206     priv->image_position = GTK_POS_LEFT;
207     priv->hbox = NULL;
208     priv->label_box = NULL;
209
210     gtk_widget_set_name (GTK_WIDGET (priv->title), "hildon-button-title");
211     gtk_widget_set_name (GTK_WIDGET (priv->value), "hildon-button-value");
212
213     gtk_misc_set_alignment (GTK_MISC (priv->title), 0, 0.5);
214     gtk_misc_set_alignment (GTK_MISC (priv->value), 0, 0.5);
215
216     /* The labels are not shown automatically, see hildon_button_set_(title|value) */
217     gtk_widget_set_no_show_all (GTK_WIDGET (priv->title), TRUE);
218     gtk_widget_set_no_show_all (GTK_WIDGET (priv->value), TRUE);
219 }
220
221 /**
222  * hildon_button_add_title_size_group:
223  * @button: a #HildonButton
224  * @size_group: A #GtkSizeGroup for the button title (main label)
225  *
226  * Adds the title label of @button to @size_group.
227  **/
228 void
229 hildon_button_add_title_size_group              (HildonButton *button,
230                                                  GtkSizeGroup *size_group)
231 {
232     HildonButtonPrivate *priv;
233
234     g_return_if_fail (HILDON_IS_BUTTON (button));
235     g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
236
237     priv = HILDON_BUTTON_GET_PRIVATE (button);
238
239     gtk_size_group_add_widget (size_group, GTK_WIDGET (priv->title));
240 }
241
242 /**
243  * hildon_button_add_value_size_group:
244  * @button: a #HildonButton
245  * @size_group: A #GtkSizeGroup for the button value (secondary label)
246  *
247  * Adds the value label of @button to @size_group.
248  **/
249 void
250 hildon_button_add_value_size_group              (HildonButton *button,
251                                                  GtkSizeGroup *size_group)
252 {
253     HildonButtonPrivate *priv;
254
255     g_return_if_fail (HILDON_IS_BUTTON (button));
256     g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
257
258     priv = HILDON_BUTTON_GET_PRIVATE (button);
259
260     gtk_size_group_add_widget (size_group, GTK_WIDGET (priv->value));
261 }
262
263 /**
264  * hildon_button_add_image_size_group:
265  * @button: a #HildonButton
266  * @size_group: A #GtkSizeGroup for the button image
267  *
268  * Adds the image of @button to @size_group. You must add an image
269  * using hildon_button_set_image before calling this function.
270  **/
271 void
272 hildon_button_add_image_size_group              (HildonButton *button,
273                                                  GtkSizeGroup *size_group)
274 {
275     HildonButtonPrivate *priv;
276
277     g_return_if_fail (HILDON_IS_BUTTON (button));
278     g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
279
280     priv = HILDON_BUTTON_GET_PRIVATE (button);
281
282     g_return_if_fail (GTK_IS_WIDGET (priv->image));
283
284     gtk_size_group_add_widget (size_group, GTK_WIDGET (priv->image));
285 }
286
287 /**
288  * hildon_button_add_size_groups:
289  * @button: a #HildonButton
290  * @title_size_group: A #GtkSizeGroup for the button title (main label), or %NULL
291  * @value_size_group: A #GtkSizeGroup group for the button value (secondary label), or %NULL
292  * @image_size_group: A #GtkSizeGroup group for the button image, or %NULL
293  *
294  * Convenience function to add title, value and image to size
295  * groups. %NULL size groups will be ignored.
296  **/
297 void
298 hildon_button_add_size_groups                   (HildonButton *button,
299                                                  GtkSizeGroup *title_size_group,
300                                                  GtkSizeGroup *value_size_group,
301                                                  GtkSizeGroup *image_size_group)
302 {
303     if (title_size_group)
304         hildon_button_add_title_size_group (button, title_size_group);
305
306     if (value_size_group)
307         hildon_button_add_value_size_group (button, value_size_group);
308
309     if (image_size_group)
310         hildon_button_add_image_size_group (button, image_size_group);
311 }
312
313 /**
314  * hildon_button_new:
315  * @size: Flags to set the size of the button.
316  * @arrangement: How the labels must be arranged.
317  *
318  * Creates a new #HildonButton. To add a child widget use gtk_container_add().
319  *
320  * Returns: a new #HildonButton
321  **/
322 GtkWidget *
323 hildon_button_new                               (HildonSizeType          size,
324                                                  HildonButtonArrangement arrangement)
325 {
326     return hildon_button_new_with_text (size, arrangement, NULL, NULL);
327 }
328
329 /**
330  * hildon_button_new_with_text:
331  * @size: Flags to set the size of the button.
332  * @arrangement: How the labels must be arranged.
333  * @title: Title of the button (main label), or %NULL
334  * @value: Value of the button (secondary label), or %NULL
335  *
336  * Creates a new #HildonButton with two labels, @title and @value.
337  *
338  * If you just don't want to use one of the labels, set it to
339  * %NULL. You can set it to a non-%NULL value at any time later.
340  *
341  * Returns: a new #HildonButton
342  **/
343 GtkWidget *
344 hildon_button_new_with_text                     (HildonSizeType           size,
345                                                  HildonButtonArrangement  arrangement,
346                                                  const gchar             *title,
347                                                  const gchar             *value)
348 {
349     GtkWidget *button;
350
351     /* Create widget */
352     button = g_object_new (HILDON_TYPE_BUTTON,
353                            "size", size,
354                            "title", title,
355                            "value", value,
356                            "arrangement", arrangement,
357                            NULL);
358
359     return button;
360 }
361
362 static void
363 hildon_button_set_arrangement                   (HildonButton            *button,
364                                                  HildonButtonArrangement  arrangement)
365 {
366     HildonButtonPrivate *priv;
367     guint horizontal_spacing;
368     guint vertical_spacing;
369
370     priv = HILDON_BUTTON_GET_PRIVATE (button);
371
372     /* Pack everything */
373     gtk_widget_style_get (GTK_WIDGET (button),
374                           "horizontal-spacing", &horizontal_spacing,
375                           "vertical-spacing", &vertical_spacing,
376                           NULL);
377
378     if (arrangement == HILDON_BUTTON_ARRANGEMENT_VERTICAL) {
379         priv->label_box = gtk_vbox_new (FALSE, vertical_spacing);
380     } else {
381         priv->label_box = gtk_hbox_new (FALSE, horizontal_spacing);
382     }
383
384     gtk_box_pack_start (GTK_BOX (priv->label_box), GTK_WIDGET (priv->title), FALSE, FALSE, 0);
385     gtk_box_pack_start (GTK_BOX (priv->label_box), GTK_WIDGET (priv->value), FALSE, FALSE, 0);
386
387     hildon_button_construct_child (button);
388 }
389
390 /**
391  * hildon_button_set_title:
392  * @button: a #HildonButton
393  * @title: a new title (main label) for the button, or %NULL
394  *
395  * Sets the title (main label) of @button to @title.
396  *
397  * This will clear the previously set title.
398  *
399  * If @title is set to %NULL, the title label will be hidden and the
400  * value label will be realigned.
401  **/
402 void
403 hildon_button_set_title                         (HildonButton *button,
404                                                  const gchar  *title)
405 {
406     HildonButtonPrivate *priv;
407
408     g_return_if_fail (HILDON_IS_BUTTON (button));
409
410     priv = HILDON_BUTTON_GET_PRIVATE (button);
411     gtk_label_set_text (priv->title, title);
412
413     /* If the button has no title, hide the label so the value is
414      * properly aligned */
415     if (title) {
416         hildon_button_construct_child (button);
417         gtk_widget_show (GTK_WIDGET (priv->title));
418     } else {
419         gtk_widget_hide (GTK_WIDGET (priv->title));
420     }
421
422     g_object_notify (G_OBJECT (button), "title");
423 }
424
425 /**
426  * hildon_button_set_value:
427  * @button: a #HildonButton
428  * @value: a new value (secondary label) for the button, or %NULL
429  *
430  * Sets the value (secondary label) of @button to @value.
431  *
432  * This will clear the previously set value.
433  *
434  * If @value is set to %NULL, the value label will be hidden and the
435  * title label will be realigned.
436  *
437  **/
438 void
439 hildon_button_set_value                         (HildonButton *button,
440                                                  const gchar  *value)
441 {
442     HildonButtonPrivate *priv;
443
444     g_return_if_fail (HILDON_IS_BUTTON (button));
445
446     priv = HILDON_BUTTON_GET_PRIVATE (button);
447     gtk_label_set_text (priv->value, value);
448
449     /* If the button has no value, hide the label so the title is
450      * properly aligned */
451     if (value) {
452         hildon_button_construct_child (button);
453         gtk_widget_show (GTK_WIDGET (priv->value));
454     } else {
455         gtk_widget_hide (GTK_WIDGET (priv->value));
456     }
457
458     g_object_notify (G_OBJECT (button), "value");
459 }
460
461 /**
462  * hildon_button_get_title:
463  * @button: a #HildonButton
464  *
465  * Gets the text from the main label (title) of @button, or %NULL if
466  * none has been set.
467  *
468  * Returns: The text of the title label. This string is owned by the
469  * widget and must not be modified or freed.
470  **/
471 const gchar *
472 hildon_button_get_title                         (HildonButton *button)
473 {
474     HildonButtonPrivate *priv;
475
476     g_return_val_if_fail (HILDON_IS_BUTTON (button), NULL);
477
478     priv = HILDON_BUTTON_GET_PRIVATE (button);
479
480     return gtk_label_get_text (priv->title);
481 }
482
483 /**
484  * hildon_button_get_value:
485  * @button: a #HildonButton
486  *
487  * Gets the text from the secondary label (value) of @button, or %NULL
488  * if none has been set.
489  *
490  * Returns: The text of the value label. This string is owned by the
491  * widget and must not be modified or freed.
492  **/
493 const gchar *
494 hildon_button_get_value                         (HildonButton *button)
495 {
496     HildonButtonPrivate *priv;
497
498     g_return_val_if_fail (HILDON_IS_BUTTON (button), NULL);
499
500     priv = HILDON_BUTTON_GET_PRIVATE (button);
501
502     return gtk_label_get_text (priv->value);
503 }
504
505 /**
506  * hildon_button_set_text:
507  * @button: a #HildonButton
508  * @title: new text for the button title (main label)
509  * @value: new text for the button value (secondary label)
510  *
511  * Convenience function to change both labels of a #HildonButton
512  **/
513 void
514 hildon_button_set_text                          (HildonButton *button,
515                                                  const gchar  *title,
516                                                  const gchar  *value)
517 {
518     hildon_button_set_title (button, title);
519     hildon_button_set_value (button, value);
520 }
521
522 /**
523  * hildon_button_set_image:
524  * @button: a #HildonButton
525  * @image: a widget to set as the button image
526  *
527  * Sets the image of @button to the given widget. The previous image
528  * (if any) will be removed.
529  **/
530 void
531 hildon_button_set_image                         (HildonButton *button,
532                                                  GtkWidget    *image)
533 {
534     HildonButtonPrivate *priv;
535
536     g_return_if_fail (HILDON_IS_BUTTON (button));
537     g_return_if_fail (!image || GTK_IS_WIDGET (image));
538
539     priv = HILDON_BUTTON_GET_PRIVATE (button);
540
541     /* Return if there's nothing to do */
542     if (image == priv->image)
543         return;
544
545     if (priv->image && priv->image->parent)
546         gtk_container_remove (GTK_CONTAINER (priv->image->parent), priv->image);
547
548     priv->image = image;
549
550     hildon_button_construct_child (button);
551 }
552
553 /**
554  * hildon_button_set_image_position:
555  * @button: a #HildonButton
556  * @position: the position of the image (%GTK_POS_LEFT or %GTK_POS_RIGHT)
557  *
558  * Sets the position of the image inside @button. Only left and right
559  * are supported.
560  **/
561 void
562 hildon_button_set_image_position                (HildonButton    *button,
563                                                  GtkPositionType  position)
564 {
565     HildonButtonPrivate *priv;
566
567     g_return_if_fail (HILDON_IS_BUTTON (button));
568     g_return_if_fail (position == GTK_POS_LEFT || position == GTK_POS_RIGHT);
569
570     priv = HILDON_BUTTON_GET_PRIVATE (button);
571
572     /* Return if there's nothing to do */
573     if (priv->image_position == position)
574         return;
575
576     priv->image_position = position;
577
578     hildon_button_construct_child (button);
579 }
580
581 static void
582 hildon_button_construct_child                   (HildonButton *button)
583 {
584     HildonButtonPrivate *priv = HILDON_BUTTON_GET_PRIVATE (button);
585     GtkWidget *child;
586
587     /* Don't do anything if the button is not constructed yet */
588     if (priv->label_box == NULL)
589         return;
590
591     /* Save a ref to the image if necessary */
592     if (priv->image && priv->image->parent != NULL) {
593         g_object_ref (priv->image);
594         gtk_container_remove (GTK_CONTAINER (priv->image->parent), priv->image);
595     }
596
597     /* Save a ref to the label box if necessary */
598     if (priv->label_box->parent != NULL) {
599         g_object_ref (priv->label_box);
600         gtk_container_remove (GTK_CONTAINER (priv->label_box->parent), priv->label_box);
601     }
602
603     /* Remove the child from the container and add priv->alignment */
604     child = gtk_bin_get_child (GTK_BIN (button));
605     if (child != NULL && child != priv->alignment) {
606         gtk_container_remove (GTK_CONTAINER (button), child);
607         child = NULL;
608     }
609
610     if (child == NULL) {
611         gtk_container_add (GTK_CONTAINER (button), GTK_WIDGET (priv->alignment));
612     }
613
614     /* Create a new hbox */
615     if (priv->hbox) {
616         gtk_container_remove (GTK_CONTAINER (priv->alignment), GTK_WIDGET (priv->hbox));
617     }
618     priv->hbox = GTK_BOX (gtk_hbox_new (FALSE, 10));
619     gtk_container_add (GTK_CONTAINER (priv->alignment), GTK_WIDGET (priv->hbox));
620
621     /* Pack the image and the alignment in the new hbox */
622     if (priv->image && priv->image_position == GTK_POS_LEFT)
623         gtk_box_pack_start (priv->hbox, priv->image, FALSE, FALSE, 0);
624
625     gtk_box_pack_start (priv->hbox, priv->label_box, TRUE, TRUE, 0);
626
627     if (priv->image && priv->image_position == GTK_POS_RIGHT)
628         gtk_box_pack_start (priv->hbox, priv->image, FALSE, FALSE, 0);
629
630     /* Show everything */
631     gtk_widget_show_all (GTK_WIDGET (priv->alignment));
632 }