bad22cc95f4deb5160b774531770bfe590a1c037
[hildon] / hildon / hildon-check-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-check-button
19  * @short_description: Button with a check box inside
20  *
21  * #HildonCheckButton is a button containing a label and a check box
22  * which will remain 'pressed-in' when clicked. Clicking again will
23  * make the check box toggle its state.
24  *
25  * #HildonCheckButton is similar to the #GtkCheckButton widget, but
26  * with a different appearance that combines a standard button and a
27  * check box.
28  *
29  * The state of a #HildonCheckButton can be set using
30  * hildon_check_button_set_active(), and retrieved using
31  * hildon_check_button_get_active(). The label can be set using
32  * gtk_button_set_label() and retrieved using gtk_button_get_label().
33  *
34  * <note>
35  *   <para>
36  * #HildonCheckButton does NOT support an image, so don't use
37  * gtk_button_set_image().
38  *   </para>
39  * </note>
40  *
41  * <example>
42  * <title>Using a Hildon check button</title>
43  * <programlisting>
44  * void
45  * button_toggled (HildonCheckButton *button, gpointer user_data)
46  * {
47  *     gboolean active;
48  * <!-- -->
49  *     active = hildon_check_button_get_active (button);
50  *     if (active)
51  *        g_debug ("Button is active");
52  *     else
53  *        g_debug ("Button is not active");
54  * }
55  * <!-- -->
56  * GtkWidget *
57  * create_button (void)
58  * {
59  *     GtkWidget *button;
60  * <!-- -->
61  *     button = hildon_check_button_new (HILDON_SIZE_AUTO);
62  *     gtk_button_set_label (GTK_BUTTON (button), "Click me");
63  * <!-- -->
64  *     g_signal_connect (button, "toggled", G_CALLBACK (button_toggled), NULL);
65  * <!-- -->
66  *     return button;
67  * }
68  * </programlisting>
69  * </example>
70  */
71
72 #include                                        "hildon-check-button.h"
73
74 enum {
75   TOGGLED,
76   LAST_SIGNAL
77 };
78
79 enum {
80     PROP_SIZE = 1
81 };
82
83 static guint                                    signals[LAST_SIGNAL] = { 0 };
84
85 G_DEFINE_TYPE                                   (HildonCheckButton, hildon_check_button, GTK_TYPE_BUTTON);
86
87 #define                                         HILDON_CHECK_BUTTON_GET_PRIVATE(obj) \
88                                                 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
89                                                 HILDON_TYPE_CHECK_BUTTON, HildonCheckButtonPrivate));
90
91 struct                                          _HildonCheckButtonPrivate
92 {
93     GtkCellRendererToggle *toggle_renderer;
94 };
95
96 /**
97  * hildon_check_button_toggled:
98  * @button: A #HildonCheckButton
99  *
100  * Emits the #HildonCheckButton::toggled signal on the #HildonCheckButton.
101  * There is no good reason for an application ever to call this function.
102  *
103  * Since: 2.2
104  */
105 void
106 hildon_check_button_toggled                     (HildonCheckButton *button)
107 {
108     g_return_if_fail (HILDON_IS_CHECK_BUTTON (button));
109
110     g_signal_emit (button, signals[TOGGLED], 0);
111 }
112
113 /**
114  * hildon_check_button_set_active:
115  * @button: A #HildonCheckButton
116  * @is_active: new state for the button
117  *
118  * Sets the status of a #HildonCheckButton. Set to %TRUE if you want
119  * @button to be 'pressed-in', and %FALSE to raise it. This action
120  * causes the #HildonCheckButton::toggled signal to be emitted.
121  *
122  * Since: 2.2
123  **/
124 void
125 hildon_check_button_set_active                  (HildonCheckButton *button,
126                                                  gboolean           is_active)
127 {
128     gboolean prev_is_active;
129
130     g_return_if_fail (HILDON_IS_CHECK_BUTTON (button));
131
132     prev_is_active = hildon_check_button_get_active (button);
133
134     if (prev_is_active != is_active) {
135         gtk_button_clicked (GTK_BUTTON (button));
136         gtk_widget_queue_draw (GTK_WIDGET (button));
137     }
138 }
139
140 /**
141  * hildon_check_button_get_active:
142  * @button: A #HildonCheckButton
143  *
144  * Gets the current state of @button.
145  *
146  * Return value: %TRUE if @button is active, %FALSE otherwise.
147  *
148  * Since: 2.2
149  **/
150 gboolean
151 hildon_check_button_get_active                  (HildonCheckButton *button)
152 {
153     g_return_val_if_fail (HILDON_IS_CHECK_BUTTON (button), FALSE);
154
155     return gtk_cell_renderer_toggle_get_active (button->priv->toggle_renderer);
156 }
157
158 /**
159  * hildon_check_button_new:
160  * @size: Flags indicating the size of the new button
161  *
162  * Creates a new #HildonCheckButton.
163  *
164  * Return value: A newly created #HildonCheckButton
165  *
166  * Since: 2.2
167  **/
168 GtkWidget *
169 hildon_check_button_new                         (HildonSizeType size)
170 {
171     return g_object_new (HILDON_TYPE_CHECK_BUTTON, "xalign", 0.0, "size", size, NULL);
172 }
173
174 static void
175 hildon_check_button_clicked                     (GtkButton *button)
176 {
177     HildonCheckButton *checkbutton = HILDON_CHECK_BUTTON (button);
178     gboolean current = hildon_check_button_get_active (checkbutton);
179
180     gtk_cell_renderer_toggle_set_active (checkbutton->priv->toggle_renderer, !current);
181
182     hildon_check_button_toggled (checkbutton);
183 }
184
185 static void
186 hildon_check_button_apply_style                 (GtkWidget *widget)
187 {
188     guint checkbox_size;
189     HildonCheckButtonPrivate *priv = HILDON_CHECK_BUTTON (widget)->priv;
190
191     gtk_widget_style_get (widget, "checkbox-size", &checkbox_size, NULL);
192
193     g_object_set (priv->toggle_renderer, "indicator-size", checkbox_size, NULL);
194 }
195
196 static void
197 hildon_check_button_style_set                   (GtkWidget *widget,
198                                                  GtkStyle  *previous_style)
199 {
200     if (GTK_WIDGET_CLASS (hildon_check_button_parent_class)->style_set)
201         GTK_WIDGET_CLASS (hildon_check_button_parent_class)->style_set (widget, previous_style);
202
203     hildon_check_button_apply_style (widget);
204 }
205
206 static void
207 set_property                                    (GObject      *object,
208                                                  guint         prop_id,
209                                                  const GValue *value,
210                                                  GParamSpec   *pspec)
211 {
212     switch (prop_id)
213     {
214     case PROP_SIZE:
215         hildon_gtk_widget_set_theme_size (GTK_WIDGET (object), g_value_get_flags (value));
216         break;
217     default:
218         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
219         break;
220     }
221 }
222
223 static void
224 hildon_check_button_class_init                  (HildonCheckButtonClass *klass)
225 {
226     GObjectClass *gobject_class = (GObjectClass*) klass;
227     GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
228     GtkButtonClass *button_class = (GtkButtonClass*) klass;
229
230     gobject_class->set_property = set_property;
231     widget_class->style_set = hildon_check_button_style_set;
232     button_class->clicked = hildon_check_button_clicked;
233
234     klass->toggled = NULL;
235
236     /**
237      * HildonCheckButton::toggled
238      *
239      * Emitted when the #HildonCheckButton's state is changed.
240      *
241      * Since: 2.2
242      */
243     signals[TOGGLED] =
244         g_signal_new ("toggled",
245                       G_OBJECT_CLASS_TYPE (gobject_class),
246                       G_SIGNAL_RUN_FIRST,
247                       G_STRUCT_OFFSET (HildonCheckButtonClass, toggled),
248                       NULL, NULL,
249                       g_cclosure_marshal_VOID__VOID,
250                       G_TYPE_NONE, 0);
251
252     gtk_widget_class_install_style_property (
253         widget_class,
254         g_param_spec_uint (
255             "checkbox-size",
256             "Size of the check box",
257             "Size of the check box",
258             0, G_MAXUINT, 26,
259             G_PARAM_READABLE));
260
261     g_object_class_install_property (
262         gobject_class,
263         PROP_SIZE,
264         g_param_spec_flags (
265             "size",
266             "Size",
267             "Size request for the button",
268             HILDON_TYPE_SIZE_TYPE,
269             HILDON_SIZE_AUTO,
270             G_PARAM_WRITABLE));
271
272     g_type_class_add_private (klass, sizeof (HildonCheckButtonPrivate));
273 }
274
275 static void
276 hildon_check_button_init                        (HildonCheckButton *button)
277 {
278     HildonCheckButtonPrivate *priv = HILDON_CHECK_BUTTON_GET_PRIVATE (button);
279     GtkWidget *cell_view = gtk_cell_view_new ();
280
281     /* Store private part */
282     button->priv = priv;
283
284     /* Make sure that the check box is always shown, no matter the value of gtk-button-images */
285     g_signal_connect (cell_view, "notify::visible", G_CALLBACK (gtk_widget_show), NULL);
286
287     /* Create toggle renderer and pack it into the cell view */
288     priv->toggle_renderer = GTK_CELL_RENDERER_TOGGLE (gtk_cell_renderer_toggle_new ());
289     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view),
290                                 GTK_CELL_RENDERER (priv->toggle_renderer), FALSE);
291
292     /* Add cell view to the image */
293     gtk_button_set_image (GTK_BUTTON (button), cell_view);
294
295     gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
296
297     hildon_check_button_apply_style (GTK_WIDGET (button));
298 }