]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/gimpcellrenderertoggle/gimpcellrenderertoggle.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[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                                                     const 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                                                     cairo_t         *cr,
64                                                     GtkWidget       *widget,
65                                                     const GdkRectangle  *background_area,
66                                                     const GdkRectangle  *cell_area,
67                                                     GtkCellRendererState flags);
68 static gboolean gimp_cell_renderer_toggle_activate (GtkCellRenderer *cell,
69                                                     GdkEvent        *event,
70                                                     GtkWidget       *widget,
71                                                     const gchar     *path,
72                                                     const GdkRectangle *background_area,
73                                                     const GdkRectangle *cell_area,
74                                                     GtkCellRendererState  flags);
75 static void gimp_cell_renderer_toggle_create_pixbuf (GimpCellRendererToggle *toggle,
76                                                      GtkWidget              *widget);
77
78
79 G_DEFINE_TYPE (GimpCellRendererToggle, gimp_cell_renderer_toggle,
80                GTK_TYPE_CELL_RENDERER_TOGGLE)
81
82 #define parent_class gimp_cell_renderer_toggle_parent_class
83
84 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
85
86
87 static void
88 gimp_cell_renderer_toggle_class_init (GimpCellRendererToggleClass *klass)
89 {
90   GObjectClass         *object_class = G_OBJECT_CLASS (klass);
91   GtkCellRendererClass *cell_class   = GTK_CELL_RENDERER_CLASS (klass);
92
93   toggle_cell_signals[CLICKED] =
94     g_signal_new ("clicked",
95                   G_OBJECT_CLASS_TYPE (object_class),
96                   G_SIGNAL_RUN_LAST,
97                   G_STRUCT_OFFSET (GimpCellRendererToggleClass, clicked),
98                   NULL, NULL,
99                   _gimp_widgets_marshal_VOID__STRING_FLAGS,
100                   G_TYPE_NONE, 2,
101                   G_TYPE_STRING,
102                   GDK_TYPE_MODIFIER_TYPE);
103
104   object_class->finalize     = gimp_cell_renderer_toggle_finalize;
105   object_class->get_property = gimp_cell_renderer_toggle_get_property;
106   object_class->set_property = gimp_cell_renderer_toggle_set_property;
107
108   cell_class->get_size       = gimp_cell_renderer_toggle_get_size;
109   cell_class->render         = gimp_cell_renderer_toggle_render;
110   cell_class->activate       = gimp_cell_renderer_toggle_activate;
111
112   g_object_class_install_property (object_class,
113                                    PROP_STOCK_ID,
114                                    g_param_spec_string ("stock-id",
115                                                         NULL, NULL,
116                                                         NULL,
117                                                         G_PARAM_READWRITE |
118                                                         G_PARAM_CONSTRUCT));
119   g_object_class_install_property (object_class,
120                                    PROP_STOCK_SIZE,
121                                    g_param_spec_int ("stock-size",
122                                                      NULL, NULL,
123                                                      0, G_MAXINT,
124                                                      DEFAULT_ICON_SIZE,
125                                                      G_PARAM_READWRITE |
126                                                      G_PARAM_CONSTRUCT));
127 }
128
129 static void
130 gimp_cell_renderer_toggle_init (GimpCellRendererToggle *toggle)
131 {
132 }
133
134 static void
135 gimp_cell_renderer_toggle_finalize (GObject *object)
136 {
137   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
138
139   if (toggle->stock_id)
140     {
141       g_free (toggle->stock_id);
142       toggle->stock_id = NULL;
143     }
144
145   if (toggle->pixbuf)
146     {
147       g_object_unref (toggle->pixbuf);
148       toggle->pixbuf = NULL;
149     }
150
151   G_OBJECT_CLASS (parent_class)->finalize (object);
152 }
153
154 static void
155 gimp_cell_renderer_toggle_get_property (GObject    *object,
156                                         guint       param_id,
157                                         GValue     *value,
158                                         GParamSpec *pspec)
159 {
160   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
161
162   switch (param_id)
163     {
164     case PROP_STOCK_ID:
165       g_value_set_string (value, toggle->stock_id);
166       break;
167     case PROP_STOCK_SIZE:
168       g_value_set_int (value, toggle->stock_size);
169       break;
170
171     default:
172       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
173       break;
174     }
175 }
176
177 static void
178 gimp_cell_renderer_toggle_set_property (GObject      *object,
179                                         guint         param_id,
180                                         const GValue *value,
181                                         GParamSpec   *pspec)
182 {
183   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
184
185   switch (param_id)
186     {
187     case PROP_STOCK_ID:
188       if (toggle->stock_id)
189         g_free (toggle->stock_id);
190       toggle->stock_id = g_value_dup_string (value);
191       break;
192     case PROP_STOCK_SIZE:
193       toggle->stock_size = g_value_get_int (value);
194       break;
195
196     default:
197       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
198       break;
199     }
200
201   if (toggle->pixbuf)
202     {
203       g_object_unref (toggle->pixbuf);
204       toggle->pixbuf = NULL;
205     }
206 }
207
208 static void
209 gimp_cell_renderer_toggle_get_size (GtkCellRenderer    *cell,
210                                     GtkWidget          *widget,
211                                     const GdkRectangle *cell_area,
212                                     gint               *x_offset,
213                                     gint               *y_offset,
214                                     gint               *width,
215                                     gint               *height)
216 {
217   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (cell);
218   GtkStyleContext        *context  = gtk_widget_get_style_context (widget);
219   GtkBorder               border;
220   gint                    calc_width;
221   gint                    calc_height;
222   gint                    pixbuf_width;
223   gint                    pixbuf_height;
224   gfloat                  xalign;
225   gfloat                  yalign;
226   gint                    xpad;
227   gint                    ypad;
228
229   if (! toggle->stock_id)
230     {
231       GTK_CELL_RENDERER_CLASS (parent_class)->get_size (cell,
232                                                         widget,
233                                                         cell_area,
234                                                         x_offset, y_offset,
235                                                         width, height);
236       return;
237     }
238
239   gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
240   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
241
242   gtk_style_context_get_border (context, 0, &border);
243
244   if (! toggle->pixbuf)
245     gimp_cell_renderer_toggle_create_pixbuf (toggle, widget);
246
247   pixbuf_width  = gdk_pixbuf_get_width  (toggle->pixbuf);
248   pixbuf_height = gdk_pixbuf_get_height (toggle->pixbuf);
249
250   calc_width  = (pixbuf_width + (gint) xpad * 2 + (border.left + border.right));
251   calc_height = (pixbuf_height + (gint) ypad * 2 + (border.top + border.bottom));
252
253   if (width)
254     *width  = calc_width;
255
256   if (height)
257     *height = calc_height;
258
259   if (cell_area)
260     {
261       if (x_offset)
262         {
263           *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
264                         (1.0 - xalign) : xalign) *
265                        (cell_area->width - calc_width));
266           *x_offset = MAX (*x_offset, 0);
267         }
268
269       if (y_offset)
270         {
271           *y_offset = yalign * (cell_area->height - calc_height);
272           *y_offset = MAX (*y_offset, 0);
273         }
274     }
275 }
276
277 static void
278 gimp_cell_renderer_toggle_render (GtkCellRenderer      *cell,
279                                   cairo_t              *cr,
280                                   GtkWidget            *widget,
281                                   const GdkRectangle   *background_area,
282                                   const GdkRectangle   *cell_area,
283                                   GtkCellRendererState  flags)
284 {
285   GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (cell);
286   GtkStyleContext        *context = gtk_widget_get_style_context (widget);
287   GdkRectangle            toggle_rect;
288   GdkRectangle            draw_rect;
289   GdkRectangle            clip_rect;
290   GtkStateFlags           state;
291   gboolean                active;
292   gint                    xpad;
293   gint                    ypad;
294
295   if (! toggle->stock_id)
296     {
297       GTK_CELL_RENDERER_CLASS (parent_class)->render (cell,
298                                                       cr,
299                                                       widget,
300                                                       background_area,
301                                                       cell_area,
302                                                       flags);
303       return;
304     }
305
306   gtk_cell_renderer_get_size (cell, widget, cell_area,
307                               &toggle_rect.x,
308                               &toggle_rect.y,
309                               &toggle_rect.width,
310                               &toggle_rect.height);
311
312   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
313   toggle_rect.x      += cell_area->x + xpad;
314   toggle_rect.y      += cell_area->y + ypad;
315   toggle_rect.width  -= xpad * 2;
316   toggle_rect.height -= ypad * 2;
317
318   if (toggle_rect.width <= 0 || toggle_rect.height <= 0)
319     return;
320
321   active =
322     gtk_cell_renderer_toggle_get_active (GTK_CELL_RENDERER_TOGGLE (cell));
323
324   if (!gtk_cell_renderer_get_sensitive (cell))
325     {
326       state = GTK_STATE_FLAG_INSENSITIVE;
327     }
328   else if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
329     {
330       if (gtk_widget_has_focus (widget))
331         state = GTK_STATE_FLAG_SELECTED;
332       else
333         state = GTK_STATE_FLAG_ACTIVE;
334     }
335   else
336     {
337       if (gtk_cell_renderer_toggle_get_activatable (GTK_CELL_RENDERER_TOGGLE (cell)))
338         state = 0;
339       else
340         state = GTK_STATE_FLAG_INSENSITIVE;
341     }
342
343   if ((flags & GTK_CELL_RENDERER_PRELIT) &&
344       gdk_cairo_get_clip_rectangle(cr, &clip_rect) &&
345       gdk_rectangle_intersect (&clip_rect, cell_area, &draw_rect))
346     {
347       cairo_save (cr);
348       gtk_style_context_save (context);
349       gdk_cairo_rectangle (cr, &draw_rect);
350       cairo_clip (cr);
351       gtk_render_frame (context, //gtk_widget_get_style_context (widget),
352                         cr,
353                         toggle_rect.x, toggle_rect.y,
354                         toggle_rect.width, toggle_rect.height);
355       gtk_style_context_restore (context);
356       cairo_restore (cr);
357     }
358
359   if (active)
360     {
361       GdkPixbuf *insensitive = NULL;
362       GdkPixbuf *pixbuf = toggle->pixbuf;
363       GtkBorder  border = { 1, 1, 1, 1 };
364
365 #if 0
366       /* FIXME: for some reason calling gtk_style_context_get_border
367        * makes the icon only visible on hover, so use border = 1
368        * for now as a workaround
369        */
370       gtk_style_context_get_border (context, state, &border);
371 #endif
372
373       toggle_rect.x      += border.left;
374       toggle_rect.y      += border.top;
375       toggle_rect.width  -= (border.left + border.right);
376       toggle_rect.height -= (border.top + border.bottom);
377
378       if (state & GTK_STATE_FLAG_INSENSITIVE)
379         {
380           GtkIconSource *source;
381
382           source = gtk_icon_source_new ();
383           gtk_icon_source_set_pixbuf (source, pixbuf);
384           /* The size here is arbitrary; since size isn't
385            * wildcarded in the source, it isn't supposed to be
386            * scaled by the engine function
387            */
388           gtk_icon_source_set_size (source, GTK_ICON_SIZE_SMALL_TOOLBAR);
389           gtk_icon_source_set_size_wildcarded (source, FALSE);
390
391           gtk_style_context_save (context);
392           gtk_style_context_set_state (context, GTK_STATE_FLAG_INSENSITIVE);
393           insensitive = gtk_render_icon_pixbuf (context, source, (GtkIconSize)-1);
394           gtk_style_context_restore (context);
395
396           gtk_icon_source_free (source);
397
398           pixbuf = insensitive;
399         }
400
401       if (gdk_rectangle_intersect (&draw_rect, &toggle_rect, &draw_rect))
402         {
403           gdk_cairo_set_source_pixbuf (cr, pixbuf, toggle_rect.x, toggle_rect.y);
404           gdk_cairo_rectangle (cr, &draw_rect);
405           cairo_fill (cr);
406         }
407
408       if (insensitive)
409         g_object_unref (insensitive);
410     }
411 }
412
413 static gboolean
414 gimp_cell_renderer_toggle_activate (GtkCellRenderer      *cell,
415                                     GdkEvent             *event,
416                                     GtkWidget            *widget,
417                                     const gchar          *path,
418                                     const GdkRectangle   *background_area,
419                                     const GdkRectangle   *cell_area,
420                                     GtkCellRendererState  flags)
421 {
422   GtkCellRendererToggle *toggle = GTK_CELL_RENDERER_TOGGLE (cell);
423
424   if (gtk_cell_renderer_toggle_get_activatable (toggle))
425     {
426       GdkModifierType state = 0;
427
428       GTK_CELL_RENDERER_CLASS (parent_class)->activate (cell, event, widget,
429                                                         path, background_area,
430                                                         cell_area, flags);
431
432       if (event && ((GdkEventAny *) event)->type == GDK_BUTTON_PRESS)
433         state = ((GdkEventButton *) event)->state;
434
435       gimp_cell_renderer_toggle_clicked (GIMP_CELL_RENDERER_TOGGLE (cell),
436                                          path, state);
437
438       return TRUE;
439     }
440
441   return FALSE;
442 }
443
444 static void
445 gimp_cell_renderer_toggle_create_pixbuf (GimpCellRendererToggle *toggle,
446                                          GtkWidget              *widget)
447 {
448   if (toggle->pixbuf)
449     g_object_unref (toggle->pixbuf);
450
451   toggle->pixbuf = gtk_widget_render_icon_pixbuf (widget,
452                                                   toggle->stock_id,
453                                                   toggle->stock_size);
454 }
455
456
457 /**
458  * gimp_cell_renderer_toggle_new:
459  * @stock_id: the stock_id of the icon to use for the active state
460  *
461  * Creates a custom version of the #GtkCellRendererToggle. Instead of
462  * showing the standard toggle button, it shows a stock icon if the
463  * cell is active and no icon otherwise. This cell renderer is for
464  * example used in the Layers treeview to indicate and control the
465  * layer's visibility by showing %GIMP_STOCK_VISIBLE.
466  *
467  * Return value: a new #GimpCellRendererToggle
468  *
469  * Since: GIMP 2.2
470  **/
471 GtkCellRenderer *
472 gimp_cell_renderer_toggle_new (const gchar *stock_id)
473 {
474   return g_object_new (GIMP_TYPE_CELL_RENDERER_TOGGLE,
475                        "stock_id", stock_id,
476                        NULL);
477 }
478
479 /**
480  * gimp_cell_renderer_toggle_clicked:
481  * @cell: a #GimpCellRendererToggle
482  * @path:
483  * @state:
484  *
485  * Emits the "clicked" signal from a #GimpCellRendererToggle.
486  *
487  * Since: GIMP 2.2
488  **/
489 void
490 gimp_cell_renderer_toggle_clicked (GimpCellRendererToggle *cell,
491                                    const gchar            *path,
492                                    GdkModifierType         state)
493 {
494   g_return_if_fail (GIMP_IS_CELL_RENDERER_TOGGLE (cell));
495   g_return_if_fail (path != NULL);
496
497   g_signal_emit (cell, toggle_cell_signals[CLICKED], 0, path, state);
498 }