]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/gimpcellrenderertoggle/gimpcellrenderertoggle.c
cb5b90f4950f541e97df73e955ca045ccf1fea5b
[evince.git] / cut-n-paste / gimpcellrenderertoggle / gimpcellrenderertoggle.c
1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3  *
4  * gimpcellrenderertoggle.c
5  * Copyright (C) 2003-2004  Sven Neumann <sven@gimp.org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #include <config.h>
24
25 #include "gimpwidgetsmarshal.h"
26 #include "gimpcellrenderertoggle.h"
27
28
29 #define DEFAULT_ICON_SIZE  GTK_ICON_SIZE_BUTTON
30
31
32 enum
33 {
34   CLICKED,
35   LAST_SIGNAL
36 };
37
38 enum
39 {
40   PROP_0,
41   PROP_STOCK_ID,
42   PROP_STOCK_SIZE
43 };
44
45
46 static void gimp_cell_renderer_toggle_finalize     (GObject         *object);
47 static void gimp_cell_renderer_toggle_get_property (GObject         *object,
48                                                     guint            param_id,
49                                                     GValue          *value,
50                                                     GParamSpec      *pspec);
51 static void gimp_cell_renderer_toggle_set_property (GObject         *object,
52                                                     guint            param_id,
53                                                     const GValue    *value,
54                                                     GParamSpec      *pspec);
55 static void gimp_cell_renderer_toggle_get_size     (GtkCellRenderer *cell,
56                                                     GtkWidget       *widget,
57                                                     GdkRectangle    *rectangle,
58                                                     gint            *x_offset,
59                                                     gint            *y_offset,
60                                                     gint            *width,
61                                                     gint            *height);
62 static void gimp_cell_renderer_toggle_render       (GtkCellRenderer *cell,
63 #if GTK_CHECK_VERSION (2, 90, 8)
64                                                     cairo_t         *cr,
65 #else
66                                                     GdkWindow       *window,
67 #endif
68                                                     GtkWidget       *widget,
69 #if GTK_CHECK_VERSION (2, 90, 8)
70                                                     const GdkRectangle    *background_area,
71                                                     const GdkRectangle    *cell_area,
72 #else
73                                                     GdkRectangle    *background_area,
74                                                     GdkRectangle    *cell_area,
75                                                     GdkRectangle    *expose_area,
76 #endif
77                                                     GtkCellRendererState flags);
78 static gboolean gimp_cell_renderer_toggle_activate (GtkCellRenderer *cell,
79                                                     GdkEvent        *event,
80                                                     GtkWidget       *widget,
81                                                     const gchar     *path,
82                                                     GdkRectangle    *background_area,
83                                                     GdkRectangle    *cell_area,
84                                                     GtkCellRendererState  flags);
85 static void gimp_cell_renderer_toggle_create_pixbuf (GimpCellRendererToggle *toggle,
86                                                      GtkWidget              *widget);
87
88
89 G_DEFINE_TYPE (GimpCellRendererToggle, gimp_cell_renderer_toggle,
90                GTK_TYPE_CELL_RENDERER_TOGGLE)
91
92 #define parent_class gimp_cell_renderer_toggle_parent_class
93
94 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
95
96
97 static void
98 gimp_cell_renderer_toggle_class_init (GimpCellRendererToggleClass *klass)
99 {
100   GObjectClass         *object_class = G_OBJECT_CLASS (klass);
101   GtkCellRendererClass *cell_class   = GTK_CELL_RENDERER_CLASS (klass);
102
103   toggle_cell_signals[CLICKED] =
104     g_signal_new ("clicked",
105                   G_OBJECT_CLASS_TYPE (object_class),
106                   G_SIGNAL_RUN_LAST,
107                   G_STRUCT_OFFSET (GimpCellRendererToggleClass, clicked),
108                   NULL, NULL,
109                   _gimp_widgets_marshal_VOID__STRING_FLAGS,
110                   G_TYPE_NONE, 2,
111                   G_TYPE_STRING,
112                   GDK_TYPE_MODIFIER_TYPE);
113
114   object_class->finalize     = gimp_cell_renderer_toggle_finalize;
115   object_class->get_property = gimp_cell_renderer_toggle_get_property;
116   object_class->set_property = gimp_cell_renderer_toggle_set_property;
117
118   cell_class->get_size       = gimp_cell_renderer_toggle_get_size;
119   cell_class->render         = gimp_cell_renderer_toggle_render;
120   cell_class->activate       = gimp_cell_renderer_toggle_activate;
121
122   g_object_class_install_property (object_class,
123                                    PROP_STOCK_ID,
124                                    g_param_spec_string ("stock-id",
125                                                         NULL, NULL,
126                                                         NULL,
127                                                         G_PARAM_READWRITE |
128                                                         G_PARAM_CONSTRUCT));
129   g_object_class_install_property (object_class,
130                                    PROP_STOCK_SIZE,
131                                    g_param_spec_int ("stock-size",
132                                                      NULL, NULL,
133                                                      0, G_MAXINT,
134                                                      DEFAULT_ICON_SIZE,
135                                                      G_PARAM_READWRITE |
136                                                      G_PARAM_CONSTRUCT));
137 }
138
139 static void
140 gimp_cell_renderer_toggle_init (GimpCellRendererToggle *toggle)
141 {
142 }
143
144 static void
145 gimp_cell_renderer_toggle_finalize (GObject *object)
146 {
147   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
148
149   if (toggle->stock_id)
150     {
151       g_free (toggle->stock_id);
152       toggle->stock_id = NULL;
153     }
154
155   if (toggle->pixbuf)
156     {
157       g_object_unref (toggle->pixbuf);
158       toggle->pixbuf = NULL;
159     }
160
161   G_OBJECT_CLASS (parent_class)->finalize (object);
162 }
163
164 static void
165 gimp_cell_renderer_toggle_get_property (GObject    *object,
166                                         guint       param_id,
167                                         GValue     *value,
168                                         GParamSpec *pspec)
169 {
170   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
171
172   switch (param_id)
173     {
174     case PROP_STOCK_ID:
175       g_value_set_string (value, toggle->stock_id);
176       break;
177     case PROP_STOCK_SIZE:
178       g_value_set_int (value, toggle->stock_size);
179       break;
180
181     default:
182       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
183       break;
184     }
185 }
186
187 static void
188 gimp_cell_renderer_toggle_set_property (GObject      *object,
189                                         guint         param_id,
190                                         const GValue *value,
191                                         GParamSpec   *pspec)
192 {
193   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
194
195   switch (param_id)
196     {
197     case PROP_STOCK_ID:
198       if (toggle->stock_id)
199         g_free (toggle->stock_id);
200       toggle->stock_id = g_value_dup_string (value);
201       break;
202     case PROP_STOCK_SIZE:
203       toggle->stock_size = g_value_get_int (value);
204       break;
205
206     default:
207       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
208       break;
209     }
210
211   if (toggle->pixbuf)
212     {
213       g_object_unref (toggle->pixbuf);
214       toggle->pixbuf = NULL;
215     }
216 }
217
218 static void
219 gimp_cell_renderer_toggle_get_size (GtkCellRenderer *cell,
220                                     GtkWidget       *widget,
221                                     GdkRectangle    *cell_area,
222                                     gint            *x_offset,
223                                     gint            *y_offset,
224                                     gint            *width,
225                                     gint            *height)
226 {
227   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (cell);
228   GtkStyle               *style  = gtk_widget_get_style (widget);
229   gint                    calc_width;
230   gint                    calc_height;
231   gint                    pixbuf_width;
232   gint                    pixbuf_height;
233   gfloat                  xalign;
234   gfloat                  yalign;
235   gint                    xpad;
236   gint                    ypad;
237
238   if (! toggle->stock_id)
239     {
240       GTK_CELL_RENDERER_CLASS (parent_class)->get_size (cell,
241                                                         widget,
242                                                         cell_area,
243                                                         x_offset, y_offset,
244                                                         width, height);
245       return;
246     }
247
248   gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
249   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
250
251   if (! toggle->pixbuf)
252     gimp_cell_renderer_toggle_create_pixbuf (toggle, widget);
253
254   pixbuf_width  = gdk_pixbuf_get_width  (toggle->pixbuf);
255   pixbuf_height = gdk_pixbuf_get_height (toggle->pixbuf);
256
257   calc_width  = (pixbuf_width +
258                  (gint) xpad * 2 + style->xthickness * 2);
259   calc_height = (pixbuf_height +
260                  (gint) ypad * 2 + style->ythickness * 2);
261
262   if (width)
263     *width  = calc_width;
264
265   if (height)
266     *height = calc_height;
267
268   if (cell_area)
269     {
270       if (x_offset)
271         {
272           *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
273                         (1.0 - xalign) : xalign) *
274                        (cell_area->width - calc_width));
275           *x_offset = MAX (*x_offset, 0);
276         }
277
278       if (y_offset)
279         {
280           *y_offset = yalign * (cell_area->height - calc_height);
281           *y_offset = MAX (*y_offset, 0);
282         }
283     }
284 }
285
286 static void
287 gimp_cell_renderer_toggle_render (GtkCellRenderer      *cell,
288 #if GTK_CHECK_VERSION (2, 90, 8)
289                                   cairo_t         *cr,
290 #else
291                                   GdkWindow       *window,
292 #endif
293                                   GtkWidget       *widget,
294 #if GTK_CHECK_VERSION (2, 90, 8)
295                                   const GdkRectangle    *background_area,
296                                   const GdkRectangle    *cell_area,
297 #else
298                                   GdkRectangle    *background_area,
299                                   GdkRectangle    *cell_area,
300                                   GdkRectangle    *expose_area,
301 #endif
302                                   GtkCellRendererState  flags)
303 {
304   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (cell);
305   GtkStyle               *style  = gtk_widget_get_style (widget);
306   GdkRectangle            toggle_rect;
307   GdkRectangle            draw_rect;
308   GtkStateType            state;
309   gboolean                active;
310   gint                    xpad;
311   gint                    ypad;
312
313   if (! toggle->stock_id)
314     {
315       GTK_CELL_RENDERER_CLASS (parent_class)->render (cell,
316 #if GTK_CHECK_VERSION (2, 90, 8)
317                                                       cr,
318 #else
319                                                       window,
320 #endif
321                                                       widget,
322                                                       background_area,
323                                                       cell_area,
324 #if !GTK_CHECK_VERSION (2, 90, 8)
325                                                       expose_area,
326 #endif
327                                                       flags);
328       return;
329     }
330
331   gtk_cell_renderer_get_size (cell, widget, cell_area,
332                               &toggle_rect.x,
333                               &toggle_rect.y,
334                               &toggle_rect.width,
335                               &toggle_rect.height);
336
337   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
338   toggle_rect.x      += cell_area->x + xpad;
339   toggle_rect.y      += cell_area->y + ypad;
340   toggle_rect.width  -= xpad * 2;
341   toggle_rect.height -= ypad * 2;
342
343   if (toggle_rect.width <= 0 || toggle_rect.height <= 0)
344     return;
345
346   active =
347     gtk_cell_renderer_toggle_get_active (GTK_CELL_RENDERER_TOGGLE (cell));
348
349   if (!gtk_cell_renderer_get_sensitive (cell))
350     {
351       state = GTK_STATE_INSENSITIVE;
352     }
353   else if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
354     {
355       if (gtk_widget_has_focus (widget))
356         state = GTK_STATE_SELECTED;
357       else
358         state = GTK_STATE_ACTIVE;
359     }
360   else
361     {
362       if (gtk_cell_renderer_toggle_get_activatable (GTK_CELL_RENDERER_TOGGLE (cell)))
363         state = GTK_STATE_NORMAL;
364       else
365         state = GTK_STATE_INSENSITIVE;
366     }
367
368 #if !GTK_CHECK_VERSION (2, 90, 8)
369   if (gdk_rectangle_intersect (expose_area, cell_area, &draw_rect) &&
370       (flags & GTK_CELL_RENDERER_PRELIT))
371     gtk_paint_shadow (style,
372                       window,
373                       state,
374                       active ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
375                       &draw_rect,
376                       widget, NULL,
377                       toggle_rect.x,     toggle_rect.y,
378                       toggle_rect.width, toggle_rect.height);
379 #endif
380
381   if (active)
382     {
383       GdkPixbuf *insensitive = NULL;
384       GdkPixbuf *pixbuf = toggle->pixbuf;
385       
386       toggle_rect.x      += style->xthickness;
387       toggle_rect.y      += style->ythickness;
388       toggle_rect.width  -= style->xthickness * 2;
389       toggle_rect.height -= style->ythickness * 2;
390
391       if (state == GTK_STATE_INSENSITIVE)
392         {
393           GtkIconSource *source;
394
395           source = gtk_icon_source_new ();
396           gtk_icon_source_set_pixbuf (source, pixbuf);
397           /* The size here is arbitrary; since size isn't
398            * wildcarded in the source, it isn't supposed to be
399            * scaled by the engine function
400            */
401           gtk_icon_source_set_size (source, GTK_ICON_SIZE_SMALL_TOOLBAR);
402           gtk_icon_source_set_size_wildcarded (source, FALSE);
403
404           insensitive = gtk_style_render_icon (gtk_widget_get_style (widget),
405                                                source,
406                                                gtk_widget_get_direction (widget),
407                                                GTK_STATE_INSENSITIVE,
408                                                /* arbitrary */
409                                                (GtkIconSize)-1,
410                                                widget,
411                                                "gimpcellrenderertoggle");
412
413           gtk_icon_source_free (source);
414
415           pixbuf = insensitive;
416         }
417
418       if (gdk_rectangle_intersect (&draw_rect, &toggle_rect, &draw_rect))
419         {
420 #if !GTK_CHECK_VERSION (2, 90, 8)
421           cairo_t *cr;
422
423           cr = gdk_cairo_create (window);
424 #endif
425
426           gdk_cairo_set_source_pixbuf (cr, pixbuf, toggle_rect.x, toggle_rect.y);
427           gdk_cairo_rectangle (cr, &draw_rect);
428           cairo_fill (cr);
429
430 #if !GTK_CHECK_VERSION (2, 90, 8)
431           cairo_destroy (cr);
432 #endif
433         }
434
435       if (insensitive)
436         g_object_unref (insensitive);
437     }
438 }
439
440 static gboolean
441 gimp_cell_renderer_toggle_activate (GtkCellRenderer      *cell,
442                                     GdkEvent             *event,
443                                     GtkWidget            *widget,
444                                     const gchar          *path,
445                                     GdkRectangle         *background_area,
446                                     GdkRectangle         *cell_area,
447                                     GtkCellRendererState  flags)
448 {
449   GtkCellRendererToggle *toggle = GTK_CELL_RENDERER_TOGGLE (cell);
450
451   if (gtk_cell_renderer_toggle_get_activatable (toggle))
452     {
453       GdkModifierType state = 0;
454
455       GTK_CELL_RENDERER_CLASS (parent_class)->activate (cell, event, widget,
456                                                         path, background_area,
457                                                         cell_area, flags);
458
459       if (event && ((GdkEventAny *) event)->type == GDK_BUTTON_PRESS)
460         state = ((GdkEventButton *) event)->state;
461
462       gimp_cell_renderer_toggle_clicked (GIMP_CELL_RENDERER_TOGGLE (cell),
463                                          path, state);
464
465       return TRUE;
466     }
467
468   return FALSE;
469 }
470
471 static void
472 gimp_cell_renderer_toggle_create_pixbuf (GimpCellRendererToggle *toggle,
473                                          GtkWidget              *widget)
474 {
475   if (toggle->pixbuf)
476     g_object_unref (toggle->pixbuf);
477
478   toggle->pixbuf = gtk_widget_render_icon (widget,
479                                            toggle->stock_id,
480                                            toggle->stock_size, NULL);
481 }
482
483
484 /**
485  * gimp_cell_renderer_toggle_new:
486  * @stock_id: the stock_id of the icon to use for the active state
487  *
488  * Creates a custom version of the #GtkCellRendererToggle. Instead of
489  * showing the standard toggle button, it shows a stock icon if the
490  * cell is active and no icon otherwise. This cell renderer is for
491  * example used in the Layers treeview to indicate and control the
492  * layer's visibility by showing %GIMP_STOCK_VISIBLE.
493  *
494  * Return value: a new #GimpCellRendererToggle
495  *
496  * Since: GIMP 2.2
497  **/
498 GtkCellRenderer *
499 gimp_cell_renderer_toggle_new (const gchar *stock_id)
500 {
501   return g_object_new (GIMP_TYPE_CELL_RENDERER_TOGGLE,
502                        "stock_id", stock_id,
503                        NULL);
504 }
505
506 /**
507  * gimp_cell_renderer_toggle_clicked:
508  * @cell: a #GimpCellRendererToggle
509  * @path:
510  * @state:
511  *
512  * Emits the "clicked" signal from a #GimpCellRendererToggle.
513  *
514  * Since: GIMP 2.2
515  **/
516 void
517 gimp_cell_renderer_toggle_clicked (GimpCellRendererToggle *cell,
518                                    const gchar            *path,
519                                    GdkModifierType         state)
520 {
521   g_return_if_fail (GIMP_IS_CELL_RENDERER_TOGGLE (cell));
522   g_return_if_fail (path != NULL);
523
524   g_signal_emit (cell, toggle_cell_signals[CLICKED], 0, path, state);
525 }