2008-07-31 Alberto Garcia <agarcia@igalia.com>
[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: title and value (the latter being
34  * optional). To change the alignment of the labels, use
35  * gtk_button_set_alignment()
36  */
37
38 #include                                        "hildon-button.h"
39 #include                                        "hildon-enum-types.h"
40
41 #define FINGER_BUTTON_HEIGHT                    70
42 #define THUMB_BUTTON_HEIGHT                     105
43 #define HALFSCREEN_BUTTON_WIDTH                 400
44 #define FULLSCREEN_BUTTON_WIDTH                 800
45
46 G_DEFINE_TYPE                                   (HildonButton, hildon_button, GTK_TYPE_BUTTON);
47
48 #define                                         HILDON_BUTTON_GET_PRIVATE(obj) \
49                                                 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
50                                                 HILDON_TYPE_BUTTON, HildonButtonPrivate));
51
52 typedef struct                                  _HildonButtonPrivate HildonButtonPrivate;
53
54 struct                                          _HildonButtonPrivate
55 {
56     GtkLabel *title;
57     GtkLabel *value;
58     GtkWidget *alignment;
59 };
60
61 enum {
62   PROP_TITLE = 1,
63   PROP_VALUE,
64   PROP_ARRANGEMENT_FLAGS
65 };
66
67 static void
68 hildon_button_set_arrangement                   (HildonButton      *button,
69                                                  HildonButtonFlags  flags);
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_ARRANGEMENT_FLAGS:
91         hildon_button_set_arrangement (button, g_value_get_flags (value));
92         break;
93     default:
94         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
95         break;
96     }
97 }
98
99 static void
100 hildon_button_get_property                      (GObject    *object,
101                                                  guint       prop_id,
102                                                  GValue     *value,
103                                                  GParamSpec *pspec)
104 {
105     HildonButton *button = HILDON_BUTTON (object);
106     HildonButtonPrivate *priv = HILDON_BUTTON_GET_PRIVATE (button);
107
108     switch (prop_id)
109     {
110     case PROP_TITLE:
111         g_value_set_string (value, gtk_label_get_text (priv->title));
112         break;
113     case PROP_VALUE:
114         g_value_set_string (value, gtk_label_get_text (priv->value));
115         break;
116     default:
117         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118         break;
119     }
120 }
121
122 static void
123 hildon_button_class_init                        (HildonButtonClass *klass)
124 {
125     GObjectClass *gobject_class = (GObjectClass *)klass;
126     GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
127
128     gobject_class->set_property = hildon_button_set_property;
129     gobject_class->get_property = hildon_button_get_property;
130
131     g_object_class_install_property (
132         gobject_class,
133         PROP_TITLE,
134         g_param_spec_string (
135             "title",
136             "Title",
137             "Text of the title label inside the button",
138             NULL,
139             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
140
141     g_object_class_install_property (
142         gobject_class,
143         PROP_VALUE,
144         g_param_spec_string (
145             "value",
146             "Value",
147             "Text of the value label inside the button",
148             NULL,
149             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
150
151     g_object_class_install_property (
152         gobject_class,
153         PROP_ARRANGEMENT_FLAGS,
154         g_param_spec_flags (
155             "arrangement-flags",
156             "Arrangement flags",
157             "How the button contents must be arranged",
158             HILDON_TYPE_BUTTON_FLAGS,
159             HILDON_BUTTON_WITH_HORIZONTAL_VALUE,
160             G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
161
162     gtk_widget_class_install_style_property (
163         widget_class,
164         g_param_spec_uint (
165             "horizontal-spacing",
166             "Horizontal spacing between labels",
167             "Horizontal spacing between the title and value labels, when in horizontal mode",
168             0, G_MAXUINT, 25,
169             G_PARAM_READABLE));
170
171     gtk_widget_class_install_style_property (
172         widget_class,
173         g_param_spec_uint (
174             "vertical-spacing",
175             "Vertical spacing between labels",
176             "Vertical spacing between the title and value labels, when in vertical mode",
177             0, G_MAXUINT, 5,
178             G_PARAM_READABLE));
179
180     g_type_class_add_private (klass, sizeof (HildonButtonPrivate));
181 }
182
183 static void
184 hildon_button_init                              (HildonButton *self)
185 {
186     HildonButtonPrivate *priv = HILDON_BUTTON_GET_PRIVATE (self);
187
188     priv->title = GTK_LABEL (gtk_label_new (NULL));
189     priv->value = GTK_LABEL (gtk_label_new (NULL));
190     priv->alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
191
192     gtk_widget_set_name (GTK_WIDGET (priv->title), "hildon-button-title");
193     gtk_widget_set_name (GTK_WIDGET (priv->value), "hildon-button-value");
194
195     gtk_misc_set_alignment (GTK_MISC (priv->title), 0, 0.5);
196     gtk_misc_set_alignment (GTK_MISC (priv->value), 0, 0.5);
197
198     /* The value label is not shown automatically, see hildon_button_set_value() */
199     gtk_widget_set_no_show_all (GTK_WIDGET (priv->value), TRUE);
200 }
201
202 /**
203  * hildon_button_set_size_groups:
204  * @button: a #HildonButton
205  * @title_size_group: A #GtkSizeGroup for the button title (main label), or %NULL
206  * @value_size_group: A #GtkSizeGroup group for the button value (secondary label), or %NULL
207  *
208  * Adds the title and value labels of @button to @title_size_group and
209  * @value_size_group respectively. %NULL size groups will be ignored.
210  **/
211 void
212 hildon_button_set_size_groups                   (HildonButton *button,
213                                                  GtkSizeGroup *title_size_group,
214                                                  GtkSizeGroup *value_size_group)
215 {
216     HildonButtonPrivate *priv;
217
218     g_return_if_fail (HILDON_IS_BUTTON (button));
219     g_return_if_fail (!title_size_group || GTK_IS_SIZE_GROUP (title_size_group));
220     g_return_if_fail (!value_size_group || GTK_IS_SIZE_GROUP (value_size_group));
221
222     priv = HILDON_BUTTON_GET_PRIVATE (button);
223
224     if (title_size_group)
225         gtk_size_group_add_widget (title_size_group, GTK_WIDGET (priv->title));
226
227     if (value_size_group)
228         gtk_size_group_add_widget (value_size_group, GTK_WIDGET (priv->value));
229 }
230
231 /**
232  * hildon_button_new:
233  * @flags: flags to set the size and layout of the button
234  *
235  * Creates a new #HildonButton. To add a child widget use gtk_container_add().
236  *
237  * Returns: a new #HildonButton
238  **/
239 GtkWidget *
240 hildon_button_new                               (HildonButtonFlags  flags)
241 {
242     return hildon_button_new_full (flags, NULL, NULL, NULL, NULL);
243 }
244
245 /**
246  * hildon_button_new_with_text:
247  * @flags: flags to set the size and layout of the button
248  * @title: Title of the button (main label)
249  * @value: Value of the button (secondary label), or %NULL
250  *
251  * Creates a new #HildonButton with two labels, @title and @value.
252  *
253  * If you just want to use the main label, set @value to %NULL. You
254  * can set it to a non-%NULL value at any time later.
255  *
256  * Returns: a new #HildonButton
257  **/
258 GtkWidget *
259 hildon_button_new_with_text                     (HildonButtonFlags  flags,
260                                                  const char        *title,
261                                                  const char        *value)
262 {
263     return hildon_button_new_full (flags, title, value, NULL, NULL);
264 }
265
266 /**
267  * hildon_button_new_full:
268  * @flags: flags to set the size and layout of the button
269  * @title: Title of the button (main label)
270  * @value: Value of the button (secondary label), or %NULL
271  * @title_size_group: a #GtkSizeGroup for the @title label, or %NULL
272  * @value_size_group: a #GtkSizeGroup for the @value label, or %NULL
273  *
274  * Creates a new #HildonButton with two labels, @title and @value, and
275  * their respective size groups.
276  *
277  * If you just want to use the main label, set @value to %NULL. You
278  * can set it to a non-%NULL value at any time later.
279  *
280  * @title and @value will be added to @title_size_group and
281  * @value_size_group, respectively, if present.
282  *
283  * Returns: a new #HildonButton
284  **/
285 GtkWidget *
286 hildon_button_new_full                          (HildonButtonFlags  flags,
287                                                  const char        *title,
288                                                  const char        *value,
289                                                  GtkSizeGroup      *title_size_group,
290                                                  GtkSizeGroup      *value_size_group)
291 {
292     GtkWidget *button;
293
294     /* Create widget */
295     button = g_object_new (HILDON_TYPE_BUTTON,
296                            "arrangement-flags", flags,
297                            "title", title,
298                            "value", value,
299                            "name", "hildon-button",
300                            NULL);
301     /* Set size groups */
302     if (title_size_group || value_size_group)
303         hildon_button_set_size_groups (HILDON_BUTTON (button), title_size_group, value_size_group);
304
305     return button;
306 }
307
308 static void
309 hildon_button_set_arrangement (HildonButton *button,
310                                HildonButtonFlags flags)
311 {
312     GtkWidget *box;
313     HildonButtonPrivate *priv;
314     guint horizontal_spacing;
315     guint vertical_spacing;
316     gint width = -1;
317     gint height = -1;
318     const char *widget_name = NULL;
319
320     priv = HILDON_BUTTON_GET_PRIVATE (button);
321
322     /* Requested height */
323     if (flags & HILDON_BUTTON_FINGER_HEIGHT) {
324         height = FINGER_BUTTON_HEIGHT;
325         widget_name = "hildon-finger-button";
326     } else if (flags & HILDON_BUTTON_THUMB_HEIGHT) {
327         height = THUMB_BUTTON_HEIGHT;
328         widget_name = "hildon-thumb-button";
329     }
330
331     if (widget_name) {
332         gtk_widget_set_name (GTK_WIDGET (button), widget_name);
333     }
334
335     /* Requested width */
336     if (flags & HILDON_BUTTON_HALFSCREEN_WIDTH) {
337         width = HALFSCREEN_BUTTON_WIDTH;
338     } else if (flags & HILDON_BUTTON_FULLSCREEN_WIDTH) {
339         width = FULLSCREEN_BUTTON_WIDTH;
340     }
341
342     g_object_set (button,
343                   "width-request", width,
344                   "height-request", height,
345                   NULL);
346
347     /* Pack everything */
348     gtk_widget_style_get (GTK_WIDGET (button),
349                           "horizontal-spacing", &horizontal_spacing,
350                           "vertical-spacing", &vertical_spacing,
351                           NULL);
352
353     if (flags & HILDON_BUTTON_WITH_VERTICAL_VALUE) {
354         box = gtk_vbox_new (FALSE, vertical_spacing);
355     } else {
356         box = gtk_hbox_new (FALSE, horizontal_spacing);
357     }
358
359     gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (priv->title), TRUE, TRUE, 0);
360     gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (priv->value), TRUE, TRUE, 0);
361
362     gtk_container_add (GTK_CONTAINER (priv->alignment), box);
363 }
364
365 /**
366  * hildon_button_set_title:
367  * @button: a #HildonButton
368  * @title: a new title (main label) for the button.
369  *
370  * Sets the title (main label) of @button to @title.
371  *
372  * This will clear the previously set title.
373  **/
374 void
375 hildon_button_set_title                         (HildonButton *button,
376                                                  const char   *title)
377 {
378     HildonButtonPrivate *priv;
379
380     g_return_if_fail (HILDON_IS_BUTTON (button));
381
382     priv = HILDON_BUTTON_GET_PRIVATE (button);
383     gtk_label_set_text (priv->title, title);
384
385     if (title)
386         hildon_button_construct_child (button);
387
388     g_object_notify (G_OBJECT (button), "title");
389 }
390
391 /**
392  * hildon_button_set_value:
393  * @button: a #HildonButton
394  * @value: a new value (secondary label) for the button, or %NULL
395  *
396  * Sets the value (secondary label) of @button to @value.
397  *
398  * This will clear the previously set value.
399  *
400  * If @value is set to %NULL, the value label will be hidden and the
401  * title label will be realigned.
402  *
403  **/
404 void
405 hildon_button_set_value                         (HildonButton *button,
406                                                  const char   *value)
407 {
408     HildonButtonPrivate *priv;
409
410     g_return_if_fail (HILDON_IS_BUTTON (button));
411
412     priv = HILDON_BUTTON_GET_PRIVATE (button);
413     gtk_label_set_text (priv->value, value);
414
415     /* If the button has no value, hide the label so the title is
416      * properly aligned */
417     if (value)
418         gtk_widget_show (GTK_WIDGET (priv->value));
419     else
420         gtk_widget_hide (GTK_WIDGET (priv->value));
421
422     g_object_notify (G_OBJECT (button), "value");
423 }
424
425 /**
426  * hildon_button_get_title:
427  * @button: a #HildonButton
428  *
429  * Gets the text from the main label (title) of @button, or %NULL if
430  * none has been set.
431  *
432  * Returns: The text of the title label. This string is owned by the
433  * widget and must not be modified or freed.
434  **/
435 const char *
436 hildon_button_get_title                         (HildonButton *button)
437 {
438     HildonButtonPrivate *priv;
439
440     g_return_val_if_fail (HILDON_IS_BUTTON (button), NULL);
441
442     priv = HILDON_BUTTON_GET_PRIVATE (button);
443
444     return gtk_label_get_text (priv->title);
445 }
446
447 /**
448  * hildon_button_get_value:
449  * @button: a #HildonButton
450  *
451  * Gets the text from the secondary label (value) of @button, or %NULL
452  * if none has been set.
453  *
454  * Returns: The text of the value label. This string is owned by the
455  * widget and must not be modified or freed.
456  **/
457 const char *
458 hildon_button_get_value                         (HildonButton *button)
459 {
460     HildonButtonPrivate *priv;
461
462     g_return_val_if_fail (HILDON_IS_BUTTON (button), NULL);
463
464     priv = HILDON_BUTTON_GET_PRIVATE (button);
465
466     return gtk_label_get_text (priv->value);
467 }
468
469 /**
470  * hildon_button_set_text:
471  * @button: a #HildonButton
472  * @title: new text for the button title (main label)
473  * @value: new text for the button value (secondary label)
474  *
475  * Convenience function to change both labels of a #HildonButton
476  **/
477 void
478 hildon_button_set_text                          (HildonButton *button,
479                                                  const char   *title,
480                                                  const char   *value)
481 {
482     hildon_button_set_title (button, title);
483     hildon_button_set_value (button, value);
484 }
485
486 static void
487 hildon_button_construct_child                   (HildonButton *button)
488 {
489     HildonButtonPrivate *priv = HILDON_BUTTON_GET_PRIVATE (button);
490     GtkBin *bin = GTK_BIN (button);
491
492     /* Return if there's nothing to do */
493     if (bin->child == priv->alignment)
494         return;
495
496     if (bin->child) {
497         gtk_container_remove (GTK_CONTAINER (button), bin->child);
498     }
499
500     gtk_container_add (GTK_CONTAINER (button), priv->alignment);
501     gtk_widget_show_all (priv->alignment);
502 }