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