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