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