]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrenderertoggle.c
Fixes #136082 and #135265, patch by Morten Welinder.
[~andy/gtk] / gtk / gtkcellrenderertoggle.c
1 /* gtkcellrenderertoggle.c
2  * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include "gtkcellrenderertoggle.h"
23 #include "gtkintl.h"
24 #include "gtkmarshalers.h"
25 #include "gtktreeprivate.h"
26
27 static void gtk_cell_renderer_toggle_get_property  (GObject                    *object,
28                                                     guint                       param_id,
29                                                     GValue                     *value,
30                                                     GParamSpec                 *pspec);
31 static void gtk_cell_renderer_toggle_set_property  (GObject                    *object,
32                                                     guint                       param_id,
33                                                     const GValue               *value,
34                                                     GParamSpec                 *pspec);
35 static void gtk_cell_renderer_toggle_init       (GtkCellRendererToggle      *celltext);
36 static void gtk_cell_renderer_toggle_class_init (GtkCellRendererToggleClass *class);
37 static void gtk_cell_renderer_toggle_get_size   (GtkCellRenderer            *cell,
38                                                  GtkWidget                  *widget,
39                                                  GdkRectangle               *cell_area,
40                                                  gint                       *x_offset,
41                                                  gint                       *y_offset,
42                                                  gint                       *width,
43                                                  gint                       *height);
44 static void gtk_cell_renderer_toggle_render     (GtkCellRenderer            *cell,
45                                                  GdkWindow                  *window,
46                                                  GtkWidget                  *widget,
47                                                  GdkRectangle               *background_area,
48                                                  GdkRectangle               *cell_area,
49                                                  GdkRectangle               *expose_area,
50                                                  GtkCellRendererState        flags);
51 static gboolean gtk_cell_renderer_toggle_activate  (GtkCellRenderer            *cell,
52                                                     GdkEvent                   *event,
53                                                     GtkWidget                  *widget,
54                                                     const gchar                *path,
55                                                     GdkRectangle               *background_area,
56                                                     GdkRectangle               *cell_area,
57                                                     GtkCellRendererState        flags);
58
59
60 enum {
61   TOGGLED,
62   LAST_SIGNAL
63 };
64
65 enum {
66   PROP_ZERO,
67   PROP_ACTIVATABLE,
68   PROP_ACTIVE,
69   PROP_RADIO,
70   PROP_INCONSISTENT
71 };
72
73 #define TOGGLE_WIDTH 12
74
75 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
76
77 #define GTK_CELL_RENDERER_TOGGLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE, GtkCellRendererTogglePrivate))
78
79 typedef struct _GtkCellRendererTogglePrivate GtkCellRendererTogglePrivate;
80 struct _GtkCellRendererTogglePrivate
81 {
82   guint inconsistent : 1;
83 };
84
85
86 GType
87 gtk_cell_renderer_toggle_get_type (void)
88 {
89   static GType cell_toggle_type = 0;
90
91   if (!cell_toggle_type)
92     {
93       static const GTypeInfo cell_toggle_info =
94       {
95         sizeof (GtkCellRendererToggleClass),
96         NULL,           /* base_init */
97         NULL,           /* base_finalize */
98         (GClassInitFunc) gtk_cell_renderer_toggle_class_init,
99         NULL,           /* class_finalize */
100         NULL,           /* class_data */
101         sizeof (GtkCellRendererToggle),
102         0,              /* n_preallocs */
103         (GInstanceInitFunc) gtk_cell_renderer_toggle_init,
104       };
105
106       cell_toggle_type =
107         g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererToggle",
108                                 &cell_toggle_info, 0);
109     }
110
111   return cell_toggle_type;
112 }
113
114 static void
115 gtk_cell_renderer_toggle_init (GtkCellRendererToggle *celltoggle)
116 {
117   celltoggle->activatable = TRUE;
118   celltoggle->active = FALSE;
119   celltoggle->radio = FALSE;
120   GTK_CELL_RENDERER (celltoggle)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
121   GTK_CELL_RENDERER (celltoggle)->xpad = 2;
122   GTK_CELL_RENDERER (celltoggle)->ypad = 2;
123 }
124
125 static void
126 gtk_cell_renderer_toggle_class_init (GtkCellRendererToggleClass *class)
127 {
128   GObjectClass *object_class = G_OBJECT_CLASS (class);
129   GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
130
131   object_class->get_property = gtk_cell_renderer_toggle_get_property;
132   object_class->set_property = gtk_cell_renderer_toggle_set_property;
133
134   cell_class->get_size = gtk_cell_renderer_toggle_get_size;
135   cell_class->render = gtk_cell_renderer_toggle_render;
136   cell_class->activate = gtk_cell_renderer_toggle_activate;
137   
138   g_object_class_install_property (object_class,
139                                    PROP_ACTIVE,
140                                    g_param_spec_boolean ("active",
141                                                          P_("Toggle state"),
142                                                          P_("The toggle state of the button"),
143                                                          FALSE,
144                                                          G_PARAM_READABLE |
145                                                          G_PARAM_WRITABLE));
146
147   g_object_class_install_property (object_class,
148                                    PROP_INCONSISTENT,
149                                    g_param_spec_boolean ("inconsistent",
150                                                          P_("Inconsistent state"),
151                                                          P_("The inconsistent state of the button"),
152                                                          FALSE,
153                                                          G_PARAM_READABLE |
154                                                          G_PARAM_WRITABLE));
155   
156   g_object_class_install_property (object_class,
157                                    PROP_ACTIVATABLE,
158                                    g_param_spec_boolean ("activatable",
159                                                          P_("Activatable"),
160                                                          P_("The toggle button can be activated"),
161                                                          TRUE,
162                                                          G_PARAM_READABLE |
163                                                          G_PARAM_WRITABLE));
164
165   g_object_class_install_property (object_class,
166                                    PROP_RADIO,
167                                    g_param_spec_boolean ("radio",
168                                                          P_("Radio state"),
169                                                          P_("Draw the toggle button as a radio button"),
170                                                          FALSE,
171                                                          G_PARAM_READABLE |
172                                                          G_PARAM_WRITABLE));
173
174
175   toggle_cell_signals[TOGGLED] =
176     g_signal_new ("toggled",
177                   G_OBJECT_CLASS_TYPE (object_class),
178                   G_SIGNAL_RUN_LAST,
179                   G_STRUCT_OFFSET (GtkCellRendererToggleClass, toggled),
180                   NULL, NULL,
181                   _gtk_marshal_VOID__STRING,
182                   G_TYPE_NONE, 1,
183                   G_TYPE_STRING);
184
185   g_type_class_add_private (object_class, sizeof (GtkCellRendererTogglePrivate));
186 }
187
188 static void
189 gtk_cell_renderer_toggle_get_property (GObject     *object,
190                                        guint        param_id,
191                                        GValue      *value,
192                                        GParamSpec  *pspec)
193 {
194   GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object);
195   GtkCellRendererTogglePrivate *priv;
196
197   priv = GTK_CELL_RENDERER_TOGGLE_GET_PRIVATE (object);
198   
199   switch (param_id)
200     {
201     case PROP_ACTIVE:
202       g_value_set_boolean (value, celltoggle->active);
203       break;
204     case PROP_INCONSISTENT:
205       g_value_set_boolean (value, priv->inconsistent);
206       break;
207     case PROP_ACTIVATABLE:
208       g_value_set_boolean (value, celltoggle->activatable);
209       break;
210     case PROP_RADIO:
211       g_value_set_boolean (value, celltoggle->radio);
212       break;
213     default:
214       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
215       break;
216     }
217 }
218
219
220 static void
221 gtk_cell_renderer_toggle_set_property (GObject      *object,
222                                        guint         param_id,
223                                        const GValue *value,
224                                        GParamSpec   *pspec)
225 {
226   GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object);
227   GtkCellRendererTogglePrivate *priv;
228
229   priv = GTK_CELL_RENDERER_TOGGLE_GET_PRIVATE (object);
230   
231   switch (param_id)
232     {
233     case PROP_ACTIVE:
234       celltoggle->active = g_value_get_boolean (value);
235       g_object_notify (G_OBJECT(object), "active");
236       break;
237     case PROP_INCONSISTENT:
238       priv->inconsistent = g_value_get_boolean (value);
239       g_object_notify (G_OBJECT (object), "inconsistent");
240       break;
241     case PROP_ACTIVATABLE:
242       celltoggle->activatable = g_value_get_boolean (value);
243       g_object_notify (G_OBJECT(object), "activatable");
244       break;
245     case PROP_RADIO:
246       celltoggle->radio = g_value_get_boolean (value);
247       g_object_notify (G_OBJECT(object), "radio");
248       break;
249     default:
250       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
251       break;
252     }
253 }
254
255 /**
256  * gtk_cell_renderer_toggle_new:
257  * 
258  * Creates a new #GtkCellRendererToggle. Adjust rendering
259  * parameters using object properties. Object properties can be set
260  * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
261  * can bind a property to a value in a #GtkTreeModel. For example, you
262  * can bind the "active" property on the cell renderer to a boolean value
263  * in the model, thus causing the check button to reflect the state of
264  * the model.
265  * 
266  * Return value: the new cell renderer
267  **/
268 GtkCellRenderer *
269 gtk_cell_renderer_toggle_new (void)
270 {
271   return g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE, NULL);
272 }
273
274 static void
275 gtk_cell_renderer_toggle_get_size (GtkCellRenderer *cell,
276                                    GtkWidget       *widget,
277                                    GdkRectangle    *cell_area,
278                                    gint            *x_offset,
279                                    gint            *y_offset,
280                                    gint            *width,
281                                    gint            *height)
282 {
283   gint calc_width;
284   gint calc_height;
285
286   calc_width = (gint) cell->xpad * 2 + TOGGLE_WIDTH;
287   calc_height = (gint) cell->ypad * 2 + TOGGLE_WIDTH;
288
289   if (width)
290     *width = calc_width;
291
292   if (height)
293     *height = calc_height;
294
295   if (cell_area)
296     {
297       if (x_offset)
298         {
299           *x_offset = ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
300                        (1.0 - cell->xalign) : cell->xalign) * (cell_area->width - calc_width);
301           *x_offset = MAX (*x_offset, 0);
302         }
303       if (y_offset)
304         {
305           *y_offset = cell->yalign * (cell_area->height - calc_height);
306           *y_offset = MAX (*y_offset, 0);
307         }
308     }
309 }
310
311 static void
312 gtk_cell_renderer_toggle_render (GtkCellRenderer      *cell,
313                                  GdkDrawable          *window,
314                                  GtkWidget            *widget,
315                                  GdkRectangle         *background_area,
316                                  GdkRectangle         *cell_area,
317                                  GdkRectangle         *expose_area,
318                                  GtkCellRendererState  flags)
319 {
320   GtkCellRendererToggle *celltoggle = (GtkCellRendererToggle *) cell;
321   GtkCellRendererTogglePrivate *priv;
322   gint width, height;
323   gint x_offset, y_offset;
324   GtkShadowType shadow;
325   GtkStateType state = 0;
326
327   priv = GTK_CELL_RENDERER_TOGGLE_GET_PRIVATE (cell);
328
329   gtk_cell_renderer_toggle_get_size (cell, widget, cell_area,
330                                      &x_offset, &y_offset,
331                                      &width, &height);
332   width -= cell->xpad*2;
333   height -= cell->ypad*2;
334
335   if (width <= 0 || height <= 0)
336     return;
337
338   if (priv->inconsistent)
339     shadow = GTK_SHADOW_ETCHED_IN;
340   else
341     shadow = celltoggle->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
342
343   if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
344     {
345       if (GTK_WIDGET_HAS_FOCUS (widget))
346         state = GTK_STATE_SELECTED;
347       else
348         state = GTK_STATE_ACTIVE;
349     }
350   else
351     {
352       if (celltoggle->activatable)
353         state = GTK_STATE_NORMAL;
354       else
355         state = GTK_STATE_INSENSITIVE;
356     }
357
358   if (celltoggle->radio)
359     {
360       gtk_paint_option (widget->style,
361                         window,
362                         state, shadow,
363                         expose_area, widget, "cellradio",
364                         cell_area->x + x_offset + cell->xpad,
365                         cell_area->y + y_offset + cell->ypad,
366                         width - 1, height - 1);
367     }
368   else
369     {
370       gtk_paint_check (widget->style,
371                        window,
372                        state, shadow,
373                        expose_area, widget, "cellcheck",
374                        cell_area->x + x_offset + cell->xpad,
375                        cell_area->y + y_offset + cell->ypad,
376                        width - 1, height - 1);
377     }
378 }
379
380 static gint
381 gtk_cell_renderer_toggle_activate (GtkCellRenderer      *cell,
382                                    GdkEvent             *event,
383                                    GtkWidget            *widget,
384                                    const gchar          *path,
385                                    GdkRectangle         *background_area,
386                                    GdkRectangle         *cell_area,
387                                    GtkCellRendererState  flags)
388 {
389   GtkCellRendererToggle *celltoggle;
390   
391   celltoggle = GTK_CELL_RENDERER_TOGGLE (cell);
392   if (celltoggle->activatable)
393     {
394       g_signal_emit (cell, toggle_cell_signals[TOGGLED], 0, path);
395       return TRUE;
396     }
397
398   return FALSE;
399 }
400
401 /**
402  * gtk_cell_renderer_toggle_set_radio:
403  * @toggle: a #GtkCellRendererToggle
404  * @radio: %TRUE to make the toggle look like a radio button
405  * 
406  * If @radio is %TRUE, the cell renderer renders a radio toggle
407  * (i.e. a toggle in a group of mutually-exclusive toggles).
408  * If %FALSE, it renders a check toggle (a standalone boolean option).
409  * This can be set globally for the cell renderer, or changed just
410  * before rendering each cell in the model (for #GtkTreeView, you set
411  * up a per-row setting using #GtkTreeViewColumn to associate model
412  * columns with cell renderer properties).
413  **/
414 void
415 gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle,
416                                     gboolean               radio)
417 {
418   g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle));
419
420   toggle->radio = radio;
421 }
422
423 /**
424  * gtk_cell_renderer_toggle_get_radio:
425  * @toggle: a #GtkCellRendererToggle
426  *
427  * Returns wether we're rendering radio toggles rather than checkboxes. 
428  * 
429  * Return value: %TRUE if we're rendering radio toggles rather than checkboxes
430  **/
431 gboolean
432 gtk_cell_renderer_toggle_get_radio (GtkCellRendererToggle *toggle)
433 {
434   g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE);
435
436   return toggle->radio;
437 }
438
439 /**
440  * gtk_cell_renderer_toggle_get_active:
441  * @toggle: a #GtkCellRendererToggle
442  *
443  * Returns whether the cell renderer is active. See
444  * gtk_cell_renderer_toggle_set_active().
445  *
446  * Return value: %TRUE if the cell renderer is active.
447  **/
448 gboolean
449 gtk_cell_renderer_toggle_get_active (GtkCellRendererToggle *toggle)
450 {
451   g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE);
452
453   return toggle->active;
454 }
455
456 /**
457  * gtk_cell_renderer_toggle_set_active:
458  * @toggle: a #GtkCellRendererToggle.
459  * @setting: the value to set.
460  *
461  * Activates or deactivates a cell renderer.
462  **/
463 void
464 gtk_cell_renderer_toggle_set_active (GtkCellRendererToggle *toggle,
465                                      gboolean               setting)
466 {
467   g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle));
468
469   g_object_set (toggle, "active", setting ? TRUE : FALSE, NULL);
470 }