]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrenderertoggle.c
d75a5f8dc36c748260c7f29da157036ef6078113
[~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 "gtkprivate.h"
26 #include "gtktreeprivate.h"
27 #include "a11y/gtkbooleancellaccessible.h"
28
29
30 /**
31  * SECTION:gtkcellrenderertoggle
32  * @Short_description: Renders a toggle button in a cell
33  * @Title: GtkCellRendererToggle
34  *
35  * #GtkCellRendererToggle renders a toggle button in a cell. The
36  * button is drawn as a radio or a checkbutton, depending on the
37  * #GtkCellRendererToggle:radio property.
38  * When activated, it emits the #GtkCellRendererToggle::toggled signal.
39  */
40
41
42 static void gtk_cell_renderer_toggle_get_property  (GObject                    *object,
43                                                     guint                       param_id,
44                                                     GValue                     *value,
45                                                     GParamSpec                 *pspec);
46 static void gtk_cell_renderer_toggle_set_property  (GObject                    *object,
47                                                     guint                       param_id,
48                                                     const GValue               *value,
49                                                     GParamSpec                 *pspec);
50 static void gtk_cell_renderer_toggle_get_size   (GtkCellRenderer            *cell,
51                                                  GtkWidget                  *widget,
52                                                  const GdkRectangle         *cell_area,
53                                                  gint                       *x_offset,
54                                                  gint                       *y_offset,
55                                                  gint                       *width,
56                                                  gint                       *height);
57 static void gtk_cell_renderer_toggle_render     (GtkCellRenderer            *cell,
58                                                  cairo_t                    *cr,
59                                                  GtkWidget                  *widget,
60                                                  const GdkRectangle         *background_area,
61                                                  const GdkRectangle         *cell_area,
62                                                  GtkCellRendererState        flags);
63 static gboolean gtk_cell_renderer_toggle_activate  (GtkCellRenderer            *cell,
64                                                     GdkEvent                   *event,
65                                                     GtkWidget                  *widget,
66                                                     const gchar                *path,
67                                                     const GdkRectangle         *background_area,
68                                                     const GdkRectangle         *cell_area,
69                                                     GtkCellRendererState        flags);
70
71
72 enum {
73   TOGGLED,
74   LAST_SIGNAL
75 };
76
77 enum {
78   PROP_0,
79   PROP_ACTIVATABLE,
80   PROP_ACTIVE,
81   PROP_RADIO,
82   PROP_INCONSISTENT,
83   PROP_INDICATOR_SIZE
84 };
85
86 #define TOGGLE_WIDTH 16
87
88 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
89
90 struct _GtkCellRendererTogglePrivate
91 {
92   gint indicator_size;
93
94   guint active       : 1;
95   guint activatable  : 1;
96   guint inconsistent : 1;
97   guint radio        : 1;
98 };
99
100
101 G_DEFINE_TYPE (GtkCellRendererToggle, gtk_cell_renderer_toggle, GTK_TYPE_CELL_RENDERER)
102
103
104 static void
105 gtk_cell_renderer_toggle_init (GtkCellRendererToggle *celltoggle)
106 {
107   GtkCellRendererTogglePrivate *priv;
108
109   celltoggle->priv = G_TYPE_INSTANCE_GET_PRIVATE (celltoggle,
110                                                   GTK_TYPE_CELL_RENDERER_TOGGLE,
111                                                   GtkCellRendererTogglePrivate);
112   priv = celltoggle->priv;
113
114   priv->activatable = TRUE;
115   priv->active = FALSE;
116   priv->radio = FALSE;
117
118   g_object_set (celltoggle, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
119   gtk_cell_renderer_set_padding (GTK_CELL_RENDERER (celltoggle), 2, 2);
120
121   priv->indicator_size = TOGGLE_WIDTH;
122   priv->inconsistent = FALSE;
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                                                          GTK_PARAM_READWRITE));
145
146   g_object_class_install_property (object_class,
147                                    PROP_INCONSISTENT,
148                                    g_param_spec_boolean ("inconsistent",
149                                                          P_("Inconsistent state"),
150                                                          P_("The inconsistent state of the button"),
151                                                          FALSE,
152                                                          GTK_PARAM_READWRITE));
153   
154   g_object_class_install_property (object_class,
155                                    PROP_ACTIVATABLE,
156                                    g_param_spec_boolean ("activatable",
157                                                          P_("Activatable"),
158                                                          P_("The toggle button can be activated"),
159                                                          TRUE,
160                                                          GTK_PARAM_READWRITE));
161
162   g_object_class_install_property (object_class,
163                                    PROP_RADIO,
164                                    g_param_spec_boolean ("radio",
165                                                          P_("Radio state"),
166                                                          P_("Draw the toggle button as a radio button"),
167                                                          FALSE,
168                                                          GTK_PARAM_READWRITE));
169
170   g_object_class_install_property (object_class,
171                                    PROP_INDICATOR_SIZE,
172                                    g_param_spec_int ("indicator-size",
173                                                      P_("Indicator size"),
174                                                      P_("Size of check or radio indicator"),
175                                                      0,
176                                                      G_MAXINT,
177                                                      TOGGLE_WIDTH,
178                                                      GTK_PARAM_READWRITE));
179
180   
181   /**
182    * GtkCellRendererToggle::toggled:
183    * @cell_renderer: the object which received the signal
184    * @path: string representation of #GtkTreePath describing the 
185    *        event location
186    *
187    * The ::toggled signal is emitted when the cell is toggled. 
188    **/
189   toggle_cell_signals[TOGGLED] =
190     g_signal_new (I_("toggled"),
191                   G_OBJECT_CLASS_TYPE (object_class),
192                   G_SIGNAL_RUN_LAST,
193                   G_STRUCT_OFFSET (GtkCellRendererToggleClass, toggled),
194                   NULL, NULL,
195                   _gtk_marshal_VOID__STRING,
196                   G_TYPE_NONE, 1,
197                   G_TYPE_STRING);
198
199   g_type_class_add_private (object_class, sizeof (GtkCellRendererTogglePrivate));
200
201   _gtk_cell_renderer_class_set_accessible_type (cell_class, GTK_TYPE_BOOLEAN_CELL_ACCESSIBLE);
202 }
203
204 static void
205 gtk_cell_renderer_toggle_get_property (GObject     *object,
206                                        guint        param_id,
207                                        GValue      *value,
208                                        GParamSpec  *pspec)
209 {
210   GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object);
211   GtkCellRendererTogglePrivate *priv = celltoggle->priv;
212
213   switch (param_id)
214     {
215     case PROP_ACTIVE:
216       g_value_set_boolean (value, priv->active);
217       break;
218     case PROP_INCONSISTENT:
219       g_value_set_boolean (value, priv->inconsistent);
220       break;
221     case PROP_ACTIVATABLE:
222       g_value_set_boolean (value, priv->activatable);
223       break;
224     case PROP_RADIO:
225       g_value_set_boolean (value, priv->radio);
226       break;
227     case PROP_INDICATOR_SIZE:
228       g_value_set_int (value, priv->indicator_size);
229       break;
230     default:
231       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
232       break;
233     }
234 }
235
236
237 static void
238 gtk_cell_renderer_toggle_set_property (GObject      *object,
239                                        guint         param_id,
240                                        const GValue *value,
241                                        GParamSpec   *pspec)
242 {
243   GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object);
244   GtkCellRendererTogglePrivate *priv = celltoggle->priv;
245
246   switch (param_id)
247     {
248     case PROP_ACTIVE:
249       priv->active = g_value_get_boolean (value);
250       break;
251     case PROP_INCONSISTENT:
252       priv->inconsistent = g_value_get_boolean (value);
253       break;
254     case PROP_ACTIVATABLE:
255       priv->activatable = g_value_get_boolean (value);
256       break;
257     case PROP_RADIO:
258       priv->radio = g_value_get_boolean (value);
259       break;
260     case PROP_INDICATOR_SIZE:
261       priv->indicator_size = g_value_get_int (value);
262       break;
263     default:
264       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
265       break;
266     }
267 }
268
269 /**
270  * gtk_cell_renderer_toggle_new:
271  *
272  * Creates a new #GtkCellRendererToggle. Adjust rendering
273  * parameters using object properties. Object properties can be set
274  * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
275  * can bind a property to a value in a #GtkTreeModel. For example, you
276  * can bind the "active" property on the cell renderer to a boolean value
277  * in the model, thus causing the check button to reflect the state of
278  * the model.
279  *
280  * Return value: the new cell renderer
281  **/
282 GtkCellRenderer *
283 gtk_cell_renderer_toggle_new (void)
284 {
285   return g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE, NULL);
286 }
287
288 static void
289 gtk_cell_renderer_toggle_get_size (GtkCellRenderer    *cell,
290                                    GtkWidget          *widget,
291                                    const GdkRectangle *cell_area,
292                                    gint               *x_offset,
293                                    gint               *y_offset,
294                                    gint               *width,
295                                    gint               *height)
296 {
297   GtkCellRendererTogglePrivate *priv;
298   gint calc_width;
299   gint calc_height;
300   gint xpad, ypad;
301
302   priv = GTK_CELL_RENDERER_TOGGLE (cell)->priv;
303
304   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
305   calc_width = xpad * 2 + priv->indicator_size;
306   calc_height = ypad * 2 + priv->indicator_size;
307
308   if (width)
309     *width = calc_width;
310
311   if (height)
312     *height = calc_height;
313
314   if (cell_area)
315     {
316       gfloat xalign, yalign;
317
318       gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
319
320       if (x_offset)
321         {
322           *x_offset = ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
323                        (1.0 - xalign) : xalign) * (cell_area->width - calc_width);
324           *x_offset = MAX (*x_offset, 0);
325         }
326       if (y_offset)
327         {
328           *y_offset = yalign * (cell_area->height - calc_height);
329           *y_offset = MAX (*y_offset, 0);
330         }
331     }
332   else
333     {
334       if (x_offset) *x_offset = 0;
335       if (y_offset) *y_offset = 0;
336     }
337 }
338
339 static void
340 gtk_cell_renderer_toggle_render (GtkCellRenderer      *cell,
341                                  cairo_t              *cr,
342                                  GtkWidget            *widget,
343                                  const GdkRectangle   *background_area,
344                                  const GdkRectangle   *cell_area,
345                                  GtkCellRendererState  flags)
346 {
347   GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (cell);
348   GtkCellRendererTogglePrivate *priv = celltoggle->priv;
349   GtkStyleContext *context;
350   gint width, height;
351   gint x_offset, y_offset;
352   gint xpad, ypad;
353   GtkStateFlags state;
354
355   context = gtk_widget_get_style_context (widget);
356   gtk_cell_renderer_toggle_get_size (cell, widget, cell_area,
357                                      &x_offset, &y_offset,
358                                      &width, &height);
359   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
360   width -= xpad * 2;
361   height -= ypad * 2;
362
363   if (width <= 0 || height <= 0)
364     return;
365
366   state = gtk_cell_renderer_get_state (cell, widget, flags);
367
368   if (!priv->activatable)
369     state |= GTK_STATE_FLAG_INSENSITIVE;
370
371   state &= ~(GTK_STATE_FLAG_INCONSISTENT | GTK_STATE_FLAG_ACTIVE);
372
373   if (priv->inconsistent)
374     state |= GTK_STATE_FLAG_INCONSISTENT;
375   else if (priv->active)
376     state |= GTK_STATE_FLAG_ACTIVE;
377
378   cairo_save (cr);
379
380   gdk_cairo_rectangle (cr, cell_area);
381   cairo_clip (cr);
382
383   gtk_style_context_save (context);
384   gtk_style_context_set_state (context, state);
385
386   if (priv->radio)
387     {
388       gtk_style_context_add_class (context, GTK_STYLE_CLASS_RADIO);
389       gtk_render_option (context, cr,
390                          cell_area->x + x_offset + xpad,
391                          cell_area->y + y_offset + ypad,
392                          width, height);
393     }
394   else
395     {
396       gtk_style_context_add_class (context, GTK_STYLE_CLASS_CHECK);
397       gtk_render_check (context, cr,
398                         cell_area->x + x_offset + xpad,
399                         cell_area->y + y_offset + ypad,
400                         width, height);
401     }
402
403   gtk_style_context_restore (context);
404   cairo_restore (cr);
405 }
406
407 static gint
408 gtk_cell_renderer_toggle_activate (GtkCellRenderer      *cell,
409                                    GdkEvent             *event,
410                                    GtkWidget            *widget,
411                                    const gchar          *path,
412                                    const GdkRectangle   *background_area,
413                                    const GdkRectangle   *cell_area,
414                                    GtkCellRendererState  flags)
415 {
416   GtkCellRendererTogglePrivate *priv;
417   GtkCellRendererToggle *celltoggle;
418
419   celltoggle = GTK_CELL_RENDERER_TOGGLE (cell);
420   priv = celltoggle->priv;
421
422   if (priv->activatable)
423     {
424       g_signal_emit (cell, toggle_cell_signals[TOGGLED], 0, path);
425       return TRUE;
426     }
427
428   return FALSE;
429 }
430
431 /**
432  * gtk_cell_renderer_toggle_set_radio:
433  * @toggle: a #GtkCellRendererToggle
434  * @radio: %TRUE to make the toggle look like a radio button
435  * 
436  * If @radio is %TRUE, the cell renderer renders a radio toggle
437  * (i.e. a toggle in a group of mutually-exclusive toggles).
438  * If %FALSE, it renders a check toggle (a standalone boolean option).
439  * This can be set globally for the cell renderer, or changed just
440  * before rendering each cell in the model (for #GtkTreeView, you set
441  * up a per-row setting using #GtkTreeViewColumn to associate model
442  * columns with cell renderer properties).
443  **/
444 void
445 gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle,
446                                     gboolean               radio)
447 {
448   GtkCellRendererTogglePrivate *priv;
449
450   g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle));
451
452   priv = toggle->priv;
453
454   priv->radio = radio;
455 }
456
457 /**
458  * gtk_cell_renderer_toggle_get_radio:
459  * @toggle: a #GtkCellRendererToggle
460  *
461  * Returns whether we're rendering radio toggles rather than checkboxes. 
462  * 
463  * Return value: %TRUE if we're rendering radio toggles rather than checkboxes
464  **/
465 gboolean
466 gtk_cell_renderer_toggle_get_radio (GtkCellRendererToggle *toggle)
467 {
468   g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE);
469
470   return toggle->priv->radio;
471 }
472
473 /**
474  * gtk_cell_renderer_toggle_get_active:
475  * @toggle: a #GtkCellRendererToggle
476  *
477  * Returns whether the cell renderer is active. See
478  * gtk_cell_renderer_toggle_set_active().
479  *
480  * Return value: %TRUE if the cell renderer is active.
481  **/
482 gboolean
483 gtk_cell_renderer_toggle_get_active (GtkCellRendererToggle *toggle)
484 {
485   g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE);
486
487   return toggle->priv->active;
488 }
489
490 /**
491  * gtk_cell_renderer_toggle_set_active:
492  * @toggle: a #GtkCellRendererToggle.
493  * @setting: the value to set.
494  *
495  * Activates or deactivates a cell renderer.
496  **/
497 void
498 gtk_cell_renderer_toggle_set_active (GtkCellRendererToggle *toggle,
499                                      gboolean               setting)
500 {
501   g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle));
502
503   g_object_set (toggle, "active", setting ? TRUE : FALSE, NULL);
504 }
505
506 /**
507  * gtk_cell_renderer_toggle_get_activatable:
508  * @toggle: a #GtkCellRendererToggle
509  *
510  * Returns whether the cell renderer is activatable. See
511  * gtk_cell_renderer_toggle_set_activatable().
512  *
513  * Return value: %TRUE if the cell renderer is activatable.
514  *
515  * Since: 2.18
516  **/
517 gboolean
518 gtk_cell_renderer_toggle_get_activatable (GtkCellRendererToggle *toggle)
519 {
520   g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE);
521
522   return toggle->priv->activatable;
523 }
524
525 /**
526  * gtk_cell_renderer_toggle_set_activatable:
527  * @toggle: a #GtkCellRendererToggle.
528  * @setting: the value to set.
529  *
530  * Makes the cell renderer activatable.
531  *
532  * Since: 2.18
533  **/
534 void
535 gtk_cell_renderer_toggle_set_activatable (GtkCellRendererToggle *toggle,
536                                           gboolean               setting)
537 {
538   GtkCellRendererTogglePrivate *priv;
539
540   g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle));
541
542   priv = toggle->priv;
543
544   if (priv->activatable != setting)
545     {
546       priv->activatable = setting ? TRUE : FALSE;
547       g_object_notify (G_OBJECT (toggle), "activatable");
548     }
549 }