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