Let image loader backend decide how to process image buffer
[geeqie.git] / src / cellrenderericon.cc
1 /*
2  * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include "cellrenderericon.h"
21
22 #include <cstddef>
23
24 #include <cairo.h>
25 #include <gdk/gdk.h>
26
27 enum {
28         FIXED_ICON_SIZE_MAX = 512
29 };
30
31
32 static void gqv_cell_renderer_icon_get_property(GObject         *object,
33                                                 guint           param_id,
34                                                 GValue          *value,
35                                                 GParamSpec      *pspec);
36 static void gqv_cell_renderer_icon_set_property(GObject         *object,
37                                                 guint           param_id,
38                                                 const GValue    *value,
39                                                 GParamSpec      *pspec);
40 static void gqv_cell_renderer_icon_init_wrapper(void *, void *);
41 static void gqv_cell_renderer_icon_init(GQvCellRendererIcon *cellicon);
42 static void gqv_cell_renderer_icon_class_init_wrapper(void *, void *);
43 static void gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *icon_class);
44 static void gqv_cell_renderer_icon_finalize(GObject *object);
45 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer    *cell,
46                                             GtkWidget          *widget,
47                                             const GdkRectangle *cell_area,
48                                             gint               *x_offset,
49                                             gint               *y_offset,
50                                             gint               *width,
51                                             gint               *height);
52
53 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
54                                            cairo_t *cr,
55                                            GtkWidget *widget,
56                                            const GdkRectangle *background_area,
57                                            const GdkRectangle *cell_area,
58                                            GtkCellRendererState flags);
59
60 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer      *cell,
61                                                 GdkEvent             *event,
62                                                 GtkWidget            *widget,
63                                                 const gchar          *path,
64                                                 const GdkRectangle   *background_area,
65                                                 const GdkRectangle   *cell_area,
66                                                 GtkCellRendererState  flags);
67 enum {
68   TOGGLED,
69   LAST_SIGNAL
70 };
71
72 enum {
73         PROP_ZERO,
74         PROP_PIXBUF,
75         PROP_TEXT,
76         PROP_BACKGROUND_GDK,
77         PROP_FOREGROUND_GDK,
78         PROP_FOCUSED,
79         PROP_FIXED_WIDTH,
80         PROP_FIXED_HEIGHT,
81
82         PROP_BACKGROUND_SET,
83         PROP_FOREGROUND_SET,
84         PROP_SHOW_TEXT,
85         PROP_SHOW_MARKS,
86         PROP_NUM_MARKS,
87         PROP_MARKS,
88         PROP_TOGGLED
89 };
90
91 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
92
93 static gpointer parent_class;
94
95 GType
96 gqv_cell_renderer_icon_get_type()
97 {
98         static GType cell_icon_type = 0;
99
100         if (!cell_icon_type)
101                 {
102                 static const GTypeInfo cell_icon_info =
103                         {
104                         sizeof(GQvCellRendererIconClass), /* class_size */
105                         nullptr,                /* base_init */
106                         nullptr,                /* base_finalize */
107                         static_cast<GClassInitFunc>(gqv_cell_renderer_icon_class_init_wrapper), /* class_init */
108                         nullptr,                /* class_finalize */
109                         nullptr,                /* class_data */
110                         sizeof(GQvCellRendererIcon), /* instance_size */
111                         0,              /* n_preallocs */
112                         reinterpret_cast<GInstanceInitFunc>(gqv_cell_renderer_icon_init_wrapper), /* instance_init */
113                         nullptr,                /* value_table */
114                         };
115
116                 cell_icon_type = g_type_register_static(GTK_TYPE_CELL_RENDERER,
117                                                         "GQvCellRendererIcon",
118                                                         &cell_icon_info, static_cast<GTypeFlags>(0));
119                 }
120
121         return cell_icon_type;
122 }
123
124 static void
125 gqv_cell_renderer_icon_init_wrapper(void *data, void *)
126 {
127         gqv_cell_renderer_icon_init(static_cast<GQvCellRendererIcon *>(data));
128 }
129
130 static void
131 gqv_cell_renderer_icon_init(GQvCellRendererIcon *cellicon)
132 {
133         g_object_set(G_OBJECT(cellicon), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
134         gtk_cell_renderer_set_padding(GTK_CELL_RENDERER(cellicon), 2, 2);
135 }
136
137 static void
138 gqv_cell_renderer_icon_class_init_wrapper(void *data, void *)
139 {
140         gqv_cell_renderer_icon_class_init(static_cast<GQvCellRendererIconClass *>(data));
141 }
142
143 static void
144 gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *icon_class)
145 {
146         GObjectClass *object_class = G_OBJECT_CLASS(icon_class);
147         GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(icon_class);
148
149         parent_class = g_type_class_peek_parent(icon_class);
150
151         object_class->finalize = gqv_cell_renderer_icon_finalize;
152
153         object_class->get_property = gqv_cell_renderer_icon_get_property;
154         object_class->set_property = gqv_cell_renderer_icon_set_property;
155
156         cell_class->get_size = gqv_cell_renderer_icon_get_size;
157         cell_class->render = gqv_cell_renderer_icon_render;
158         cell_class->activate = gqv_cell_renderer_icon_activate;
159
160         g_object_class_install_property(object_class,
161                                         PROP_PIXBUF,
162                                         g_param_spec_object("pixbuf",
163                                                         "Pixbuf Object",
164                                                         "The pixbuf to render",
165                                                         GDK_TYPE_PIXBUF,
166                                                         G_PARAM_READWRITE));
167
168         g_object_class_install_property(object_class,
169                                         PROP_TEXT,
170                                         g_param_spec_string("text",
171                                                         "Text",
172                                                         "Text to render",
173                                                         nullptr,
174                                                         G_PARAM_READWRITE));
175
176         g_object_class_install_property(object_class,
177                                         PROP_BACKGROUND_GDK,
178                                         g_param_spec_boxed("background_rgba",
179                                                         "Background color",
180                                                         "Background color as a GdkRGBA",
181                                                         GDK_TYPE_RGBA,
182                                                         G_PARAM_READWRITE));
183
184         g_object_class_install_property(object_class,
185                                         PROP_FOREGROUND_GDK,
186                                         g_param_spec_boxed("foreground_rgba",
187                                                         "Foreground color",
188                                                         "Foreground color as a GdkRGBA",
189                                                         GDK_TYPE_RGBA,
190                                                         G_PARAM_READWRITE));
191
192         g_object_class_install_property(object_class,
193                                         PROP_FOCUSED,
194                                         g_param_spec_boolean("has_focus",
195                                                         "Focus",
196                                                         "Draw focus indicator",
197                                                         FALSE,
198                                                         G_PARAM_READWRITE));
199
200         g_object_class_install_property(object_class,
201                                         PROP_FIXED_WIDTH,
202                                         g_param_spec_int("fixed_width",
203                                                         "Fixed width",
204                                                         "Width of cell",
205                                                         -1, FIXED_ICON_SIZE_MAX,
206                                                         -1,
207                                                         G_PARAM_READWRITE));
208
209         g_object_class_install_property(object_class,
210                                         PROP_FIXED_HEIGHT,
211                                         g_param_spec_int("fixed_height",
212                                                         "Fixed height",
213                                                         "Height of icon excluding text",
214                                                         -1, FIXED_ICON_SIZE_MAX,
215                                                         -1,
216                                                         G_PARAM_READWRITE));
217
218         g_object_class_install_property(object_class,
219                                         PROP_BACKGROUND_SET,
220                                         g_param_spec_boolean("background_set",
221                                                         "Background set",
222                                                         "Whether this tag affects the background color",
223                                                         FALSE,
224                                                         G_PARAM_READWRITE));
225
226         g_object_class_install_property(object_class,
227                                         PROP_FOREGROUND_SET,
228                                         g_param_spec_boolean("foreground_set",
229                                                         "Foreground set",
230                                                         "Whether this tag affects the foreground color",
231                                                         FALSE,
232                                                         G_PARAM_READWRITE));
233
234         g_object_class_install_property(object_class,
235                                         PROP_SHOW_TEXT,
236                                         g_param_spec_boolean("show_text",
237                                                         "Show text",
238                                                         "Whether the text is displayed",
239                                                         TRUE,
240                                                         G_PARAM_READWRITE));
241
242         g_object_class_install_property(object_class,
243                                         PROP_SHOW_MARKS,
244                                         g_param_spec_boolean("show_marks",
245                                                         "Show marks",
246                                                         "Whether the marks are displayed",
247                                                         TRUE,
248                                                         G_PARAM_READWRITE));
249
250         g_object_class_install_property(object_class,
251                                         PROP_NUM_MARKS,
252                                         g_param_spec_int("num_marks",
253                                                         "Number of marks",
254                                                         "Number of marks",
255                                                         0, 32,
256                                                         6,
257                                                         G_PARAM_READWRITE));
258
259         g_object_class_install_property(object_class,
260                                         PROP_MARKS,
261                                         g_param_spec_uint("marks",
262                                                         "Marks",
263                                                         "Marks bit array",
264                                                         0, 0xffffffff,
265                                                         0,
266                                                         G_PARAM_READWRITE));
267
268         g_object_class_install_property(object_class,
269                                         PROP_TOGGLED,
270                                         g_param_spec_uint("toggled_mark",
271                                                         "Toggled mark",
272                                                         "Toggled mark",
273                                                         0, 32,
274                                                         0,
275                                                         G_PARAM_READWRITE));
276         toggle_cell_signals[TOGGLED] =
277                 g_signal_new("toggled",
278                 G_OBJECT_CLASS_TYPE (object_class),
279                 G_SIGNAL_RUN_LAST,
280                 G_STRUCT_OFFSET (GQvCellRendererIconClass, toggled),
281                 nullptr, nullptr,
282                 g_cclosure_marshal_VOID__STRING,
283                 G_TYPE_NONE, 1,
284                 G_TYPE_STRING);
285
286 }
287
288 static void
289 gqv_cell_renderer_icon_finalize(GObject *object)
290 {
291         GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
292
293         if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
294
295         g_free(cellicon->text);
296
297         (*(G_OBJECT_CLASS(parent_class))->finalize)(object);
298 }
299
300 static void
301 gqv_cell_renderer_icon_get_property(GObject     *object,
302                                     guint       param_id,
303                                     GValue      *value,
304                                     GParamSpec  *pspec)
305 {
306         GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
307
308         switch (param_id)
309         {
310         case PROP_PIXBUF:
311                 g_value_set_object(value, cellicon->pixbuf ? G_OBJECT(cellicon->pixbuf) : nullptr);
312                 break;
313         case PROP_TEXT:
314                 g_value_set_string(value, cellicon->text);
315                 break;
316         case PROP_BACKGROUND_GDK:
317                 {
318                 GdkRGBA color;
319
320                 color.red = cellicon->background.red / 65535.0;
321                 color.green = cellicon->background.green / 65535.0;
322                 color.blue = cellicon->background.blue / 65535.0;
323
324                 g_value_set_boxed(value, &color);
325                 }
326                 break;
327         case PROP_FOREGROUND_GDK:
328                 {
329                 GdkRGBA color;
330
331                 color.red = cellicon->foreground.red / 65535.0;
332                 color.green = cellicon->foreground.green / 65535.0;
333                 color.blue = cellicon->foreground.blue / 65535.0;
334
335                 g_value_set_boxed(value, &color);
336                 }
337                 break;
338         case PROP_FOCUSED:
339                 g_value_set_boolean(value, cellicon->focused);
340                 break;
341         case PROP_FIXED_WIDTH:
342                 g_value_set_int(value, cellicon->fixed_width);
343                 break;
344         case PROP_FIXED_HEIGHT:
345                 g_value_set_int(value, cellicon->fixed_height);
346                 break;
347         case PROP_BACKGROUND_SET:
348                 g_value_set_boolean(value, cellicon->background_set);
349                 break;
350         case PROP_FOREGROUND_SET:
351                 g_value_set_boolean(value, cellicon->foreground_set);
352                 break;
353         case PROP_SHOW_TEXT:
354                 g_value_set_boolean(value, cellicon->show_text);
355                 break;
356         case PROP_SHOW_MARKS:
357                 g_value_set_boolean(value, cellicon->show_marks);
358                 break;
359         case PROP_NUM_MARKS:
360                 g_value_set_int(value, cellicon->num_marks);
361                 break;
362         case PROP_MARKS:
363                 g_value_set_uint(value, cellicon->marks);
364                 break;
365         case PROP_TOGGLED:
366                 g_value_set_uint(value, cellicon->toggled_mark);
367                 break;
368         default:
369                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
370                 break;
371         }
372 }
373
374 static void set_bg_color(GQvCellRendererIcon *cellicon, GdkRGBA *color)
375 {
376         if (color)
377                 {
378                 if (!cellicon->background_set)
379                         {
380                         cellicon->background_set = TRUE;
381                         g_object_notify(G_OBJECT(cellicon), "background_set");
382                         }
383
384                 cellicon->background.red = color->red * 65535;
385                 cellicon->background.green = color->green * 65535;
386                 cellicon->background.blue = color->blue * 65535;
387                 }
388         else
389                 {
390                 if (cellicon->background_set)
391                         {
392                         cellicon->background_set = FALSE;
393                         g_object_notify(G_OBJECT(cellicon), "background_set");
394                         }
395                 }
396 }
397
398 static void set_fg_color(GQvCellRendererIcon *cellicon, GdkRGBA *color)
399 {
400         if (color)
401                 {
402                 if (!cellicon->foreground_set)
403                         {
404                         cellicon->foreground_set = TRUE;
405                         g_object_notify(G_OBJECT(cellicon), "foreground_set");
406                         }
407                 cellicon->foreground.red = color->red * 65535;
408                 cellicon->foreground.green = color->green * 65535;
409                 cellicon->foreground.blue = color->blue * 65535;
410                 }
411         else
412                 {
413                 if (cellicon->foreground_set)
414                         {
415                         cellicon->foreground_set = FALSE;
416                         g_object_notify(G_OBJECT(cellicon), "foreground_set");
417                         }
418                 }
419 }
420
421 static void
422 gqv_cell_renderer_icon_set_property(GObject             *object,
423                                     guint               param_id,
424                                     const GValue        *value,
425                                     GParamSpec          *pspec)
426 {
427         GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
428
429         switch (param_id)
430         {
431         case PROP_PIXBUF:
432                 {
433                 GdkPixbuf *pixbuf;
434
435                 pixbuf = static_cast<GdkPixbuf *>(g_value_get_object(value));
436                 if (pixbuf) g_object_ref(pixbuf);
437                 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
438                 cellicon->pixbuf = pixbuf;
439                 }
440                 break;
441         case PROP_TEXT:
442                 {
443                 gchar *text;
444
445                 text = cellicon->text;
446                 cellicon->text = g_strdup(g_value_get_string(value));
447                 g_free(text);
448
449                 g_object_notify(object, "text");
450                 }
451                 break;
452         case PROP_BACKGROUND_GDK:
453                 set_bg_color(cellicon, static_cast<GdkRGBA *>(g_value_get_boxed(value)));
454                 break;
455         case PROP_FOREGROUND_GDK:
456                 set_fg_color(cellicon, static_cast<GdkRGBA *>(g_value_get_boxed(value)));
457                 break;
458         case PROP_FOCUSED:
459                 cellicon->focused = g_value_get_boolean(value);
460                 break;
461         case PROP_FIXED_WIDTH:
462                 cellicon->fixed_width = g_value_get_int(value);
463                 break;
464         case PROP_FIXED_HEIGHT:
465                 cellicon->fixed_height = g_value_get_int(value);
466                 break;
467         case PROP_BACKGROUND_SET:
468                 cellicon->background_set = g_value_get_boolean(value);
469                 break;
470         case PROP_FOREGROUND_SET:
471                 cellicon->foreground_set = g_value_get_boolean(value);
472                 break;
473         case PROP_SHOW_TEXT:
474                 cellicon->show_text = g_value_get_boolean(value);
475                 break;
476         case PROP_SHOW_MARKS:
477                 cellicon->show_marks = g_value_get_boolean(value);
478                 break;
479         case PROP_NUM_MARKS:
480                 cellicon->num_marks = g_value_get_int(value);
481                 break;
482         case PROP_MARKS:
483                 cellicon->marks = g_value_get_uint(value);
484                 break;
485         default:
486                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
487                 break;
488         }
489 }
490
491 static PangoLayout *
492 gqv_cell_renderer_icon_get_layout(GQvCellRendererIcon *cellicon, GtkWidget *widget, gboolean will_render)
493 {
494         PangoLayout *layout;
495         gint width;
496
497         width = (cellicon->fixed_width > 0) ? cellicon->fixed_width * PANGO_SCALE : -1;
498
499         layout = gtk_widget_create_pango_layout(widget, cellicon->text);
500         pango_layout_set_width(layout, width);
501         pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
502         pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
503
504         if (will_render)
505                 {
506                 PangoAttrList *attr_list;
507
508                 attr_list = pango_attr_list_new();
509
510                 if (cellicon->foreground_set)
511                         {
512                         PangoColor color;
513                         PangoAttribute *attr;
514
515                         color = cellicon->foreground;
516
517                         attr = pango_attr_foreground_new(color.red, color.green, color.blue);
518
519                         attr->start_index = 0;
520                         attr->end_index = G_MAXINT;
521                         pango_attr_list_insert(attr_list, attr);
522                         }
523
524                 pango_layout_set_attributes(layout, attr_list);
525                 pango_attr_list_unref(attr_list);
526                 }
527
528         return layout;
529 }
530
531 /**
532  * gqv_cell_renderer_icon_new:
533  *
534  * Creates a new #GQvCellRendererIcon. Adjust rendering
535  * parameters using object properties. Object properties can be set
536  * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
537  * can bind a property to a value in a #GtkTreeModel. For example, you
538  * can bind the "pixbuf" property on the cell renderer to a pixbuf value
539  * in the model, thus rendering a different image in each row of the
540  * #GtkTreeView.
541  *
542  * Return value: the new cell renderer
543  **/
544 GtkCellRenderer *
545 gqv_cell_renderer_icon_new()
546 {
547         return static_cast<GtkCellRenderer *>(g_object_new(GQV_TYPE_CELL_RENDERER_ICON, nullptr));
548 }
549
550 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer    *cell,
551                                             GtkWidget          *widget,
552                                             const GdkRectangle *cell_area,
553                                             gint               *x_offset,
554                                             gint               *y_offset,
555                                             gint               *width,
556                                             gint               *height)
557 {
558         auto cellicon = reinterpret_cast<GQvCellRendererIcon *>(cell);
559         gint calc_width;
560         gint calc_height;
561         gint xpad;
562         gint ypad;
563         gfloat xalign;
564         gfloat yalign;
565
566         gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
567         gtk_cell_renderer_get_alignment(cell, &xalign, &yalign);
568
569         if (cellicon->fixed_width > 0)
570                 {
571                 calc_width = cellicon->fixed_width;
572                 }
573         else
574                 {
575                 calc_width = (cellicon->pixbuf) ? gdk_pixbuf_get_width(cellicon->pixbuf) : 0;
576                 }
577
578         if (cellicon->fixed_height > 0)
579                 {
580                 calc_height = cellicon->fixed_height;
581                 }
582         else
583                 {
584                 calc_height = (cellicon->pixbuf) ? gdk_pixbuf_get_height(cellicon->pixbuf) : 0;
585                 }
586
587         if (cellicon->show_text && cellicon->text)
588                 {
589                 PangoLayout *layout;
590                 PangoRectangle rect;
591
592                 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, FALSE);
593                 pango_layout_get_pixel_extents(layout, nullptr, &rect);
594                 g_object_unref(layout);
595
596                 calc_width = MAX(calc_width, rect.width);
597                 calc_height += rect.height;
598                 }
599
600         if (cellicon->show_marks)
601                 {
602                 calc_height += TOGGLE_SPACING;
603                 calc_width = MAX(calc_width, TOGGLE_SPACING * cellicon->num_marks);
604                 }
605
606         calc_width += xpad * 2;
607         calc_height += ypad * 2;
608
609         if (x_offset) *x_offset = 0;
610         if (y_offset) *y_offset = 0;
611
612         if (cell_area && calc_width > 0 && calc_height > 0)
613                 {
614                 if (x_offset)
615                         {
616                         *x_offset = (xalign * (cell_area->width - calc_width - 2 * xpad));
617                         *x_offset = MAX(*x_offset, 0) + xpad;
618                         }
619                 if (y_offset)
620                         {
621                         *y_offset = (yalign * (cell_area->height - calc_height - 2 * ypad));
622                         *y_offset = MAX(*y_offset, 0) + ypad;
623                         }
624                 }
625
626         if (width) *width = calc_width;
627         if (height) *height = calc_height;
628 }
629
630 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
631                                            cairo_t *cr,
632                                            GtkWidget *widget,
633                                            const GdkRectangle *,
634                                            const GdkRectangle *cell_area,
635                                            GtkCellRendererState flags)
636
637 {
638         GtkStyleContext *context = gtk_widget_get_style_context(widget);
639         auto cellicon = reinterpret_cast<GQvCellRendererIcon *>(cell);
640         GdkPixbuf *pixbuf;
641         const gchar *text;
642         GdkRectangle cell_rect;
643         GtkStateFlags state;
644         gint xpad;
645         gint ypad;
646
647
648         pixbuf = cellicon->pixbuf;
649         text = cellicon->text;
650         if (!pixbuf && !text)
651                 {
652                 return;
653                 }
654
655         gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
656
657         gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
658                                         &cell_rect.x, &cell_rect.y,
659                                         &cell_rect.width, &cell_rect.height);
660
661         cell_rect.x += xpad;
662         cell_rect.y += ypad;
663         cell_rect.width -= xpad * 2;
664         cell_rect.height -= ypad * 2;
665
666         if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
667                 {
668                 if (gtk_widget_has_focus(widget))
669                         state = GTK_STATE_FLAG_SELECTED;
670                 else
671                         state = GTK_STATE_FLAG_ACTIVE;
672                 }
673         else
674                 {
675                 if (!gtk_widget_is_sensitive(widget))
676                         state = GTK_STATE_FLAG_INSENSITIVE;
677                 else
678                         state = GTK_STATE_FLAG_NORMAL;
679                 }
680         gtk_style_context_set_state(context, state);
681
682         if (pixbuf)
683                 {
684                 GdkRectangle pix_rect;
685                 GdkRectangle draw_rect;
686
687                 pix_rect.width = gdk_pixbuf_get_width(pixbuf);
688                 pix_rect.height = gdk_pixbuf_get_height(pixbuf);
689
690                 pix_rect.x = cell_area->x + (cell_area->width - pix_rect.width) / 2;
691
692                 if (cellicon->fixed_height > 0)
693                         {
694                         pix_rect.y = cell_area->y + ypad + (cellicon->fixed_height - pix_rect.height) / 2;
695                         }
696                 else
697                         {
698                         pix_rect.y = cell_area->y + cell_rect.y;
699                         }
700
701                 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect))
702                         {
703                         gdk_cairo_set_source_pixbuf(cr, pixbuf, pix_rect.x, pix_rect.y);
704                         cairo_rectangle (cr,
705                                         draw_rect.x,
706                                         draw_rect.y,
707                                         draw_rect.width,
708                                         draw_rect.height);
709
710                         cairo_fill (cr);
711                         }
712                 }
713
714         if (cellicon->show_text && text)
715                 {
716                 PangoLayout *layout;
717                 PangoRectangle text_rect;
718                 GdkRectangle pix_rect;
719                 GdkRectangle draw_rect;
720                 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, TRUE);
721                 pango_layout_get_pixel_extents(layout, nullptr, &text_rect);
722
723                 pix_rect.width = text_rect.width;
724                 pix_rect.height = text_rect.height;
725                 pix_rect.x = cell_area->x + xpad + (cell_rect.width - text_rect.width + 1) / 2;
726                 pix_rect.y = cell_area->y + ypad + (cell_rect.height - text_rect.height);
727
728                 if (cellicon->show_marks)
729                         {
730                         pix_rect.y -= TOGGLE_SPACING;
731                         }
732
733                 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect))
734                         {
735                         gtk_render_layout(context, cr, pix_rect.x - text_rect.x, pix_rect.y, layout);
736                         }
737                 g_object_unref(layout);
738                 }
739
740         if (cellicon->show_marks)
741                 {
742                 GdkRectangle pix_rect;
743                 GdkRectangle draw_rect;
744                 gint i;
745
746                 pix_rect.width = TOGGLE_SPACING * cellicon->num_marks;
747                 pix_rect.height = TOGGLE_SPACING;
748                 pix_rect.x = cell_area->x + xpad + (cell_rect.width - pix_rect.width + 1) / 2 + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
749                 pix_rect.y = cell_area->y + ypad + (cell_rect.height - pix_rect.height) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
750
751                 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect))
752                         {
753                         for (i = 0; i < cellicon->num_marks; i++)
754                                 {
755                                 state = static_cast<GtkStateFlags>(state & ~GTK_STATE_FLAG_CHECKED);
756
757                                 if ((cellicon->marks & (1 << i)))
758                                         state = static_cast<GtkStateFlags>(state | GTK_STATE_FLAG_CHECKED);
759                                 cairo_save (cr);
760
761                                 cairo_rectangle(cr,
762                                                 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2.0,
763                                                 pix_rect.y,
764                                                 TOGGLE_WIDTH, TOGGLE_WIDTH);
765                                 cairo_clip (cr);
766
767                                 gtk_style_context_save(context);
768                                 gtk_style_context_set_state(context, state);
769
770                                 gtk_style_context_add_class(context, GTK_STYLE_CLASS_CHECK);
771
772                                 gtk_style_context_add_class(context, "marks");
773
774                                 if (state & GTK_STATE_FLAG_CHECKED)
775                                         {
776                                         gtk_render_check(context, cr,
777                                                 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2.0,
778                                                 pix_rect.y,
779                                                 TOGGLE_WIDTH, TOGGLE_WIDTH);
780                                         }
781                                 gtk_render_frame(context, cr,
782                                          pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2.0,
783                                          pix_rect.y,
784                                          TOGGLE_WIDTH, TOGGLE_WIDTH);
785
786                                 if (cellicon->focused && gtk_widget_has_focus(widget))
787                                         {
788                                         gtk_render_focus(context, cr,
789                                                 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2.0,
790                                                 pix_rect.y, TOGGLE_WIDTH, TOGGLE_WIDTH);
791                                         }
792                                 gtk_style_context_restore(context);
793                                 cairo_restore(cr);
794                                 }
795                         }
796                 }
797 }
798
799 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer      *cell,
800                                                 GdkEvent             *event,
801                                                 GtkWidget            *widget,
802                                                 const gchar          *path,
803                                                 const GdkRectangle   *,
804                                                 const GdkRectangle   *cell_area,
805                                                 GtkCellRendererState)
806 {
807         auto cellicon = reinterpret_cast<GQvCellRendererIcon *>(cell);
808         GdkEventButton *bevent = &event->button;
809
810         if (cellicon->show_marks &&
811             event->type == GDK_BUTTON_PRESS &&
812             !(bevent->state & GDK_SHIFT_MASK ) &&
813             !(bevent->state & GDK_CONTROL_MASK ))
814                 {
815                 GdkRectangle rect;
816                 GdkRectangle cell_rect;
817                 gint i;
818                 gint xpad;
819                 gint ypad;
820
821                 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
822
823                 gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
824                                                 &cell_rect.x, &cell_rect.y,
825                                                 &cell_rect.width, &cell_rect.height);
826
827                 cell_rect.x += xpad;
828                 cell_rect.y += ypad;
829                 cell_rect.width -= xpad * 2;
830                 cell_rect.height -= ypad * 2;
831
832                 rect.width = TOGGLE_WIDTH;
833                 rect.height = TOGGLE_WIDTH;
834                 rect.y = cell_area->y + ypad + (cell_rect.height - TOGGLE_SPACING) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
835                 for (i = 0; i < cellicon->num_marks; i++)
836                         {
837                         rect.x = cell_area->x + xpad + (cell_rect.width - TOGGLE_SPACING * cellicon->num_marks + 1) / 2 + i * TOGGLE_SPACING;
838
839                         if (bevent->x >= rect.x && bevent->x < rect.x + rect.width &&
840                             bevent->y >= rect.y && bevent->y < rect.y + rect.height)
841                                 {
842                                 cellicon->toggled_mark = i;
843                                 g_signal_emit(cell, toggle_cell_signals[TOGGLED], 0, path);
844                                 break;
845                                 }
846                         }
847                 }
848         return FALSE;
849 }
850 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */