2 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
3 * Copyright (C) 2008 - 2016 The Geeqie Team
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.
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.
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.
21 #include "cellrenderericon.h"
24 FIXED_ICON_SIZE_MAX = 512
28 static void gqv_cell_renderer_icon_get_property(GObject *object,
32 static void gqv_cell_renderer_icon_set_property(GObject *object,
36 static void gqv_cell_renderer_icon_init_wrapper(void *, void *);
37 static void gqv_cell_renderer_icon_init(GQvCellRendererIcon *cellicon);
38 static void gqv_cell_renderer_icon_class_init_wrapper(void *, void *);
39 static void gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *icon_class);
40 static void gqv_cell_renderer_icon_finalize(GObject *object);
41 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
43 const GdkRectangle *cell_area,
49 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
52 const GdkRectangle *background_area,
53 const GdkRectangle *cell_area,
54 GtkCellRendererState flags);
56 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
60 const GdkRectangle *background_area,
61 const GdkRectangle *cell_area,
62 GtkCellRendererState flags);
87 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
89 static gpointer parent_class;
92 gqv_cell_renderer_icon_get_type()
94 static GType cell_icon_type = 0;
98 static const GTypeInfo cell_icon_info =
100 sizeof(GQvCellRendererIconClass), /* class_size */
101 nullptr, /* base_init */
102 nullptr, /* base_finalize */
103 static_cast<GClassInitFunc>(gqv_cell_renderer_icon_class_init_wrapper), /* class_init */
104 nullptr, /* class_finalize */
105 nullptr, /* class_data */
106 sizeof(GQvCellRendererIcon), /* instance_size */
108 reinterpret_cast<GInstanceInitFunc>(gqv_cell_renderer_icon_init_wrapper), /* instance_init */
109 nullptr, /* value_table */
112 cell_icon_type = g_type_register_static(GTK_TYPE_CELL_RENDERER,
113 "GQvCellRendererIcon",
114 &cell_icon_info, static_cast<GTypeFlags>(0));
117 return cell_icon_type;
121 gqv_cell_renderer_icon_init_wrapper(void *data, void *)
123 gqv_cell_renderer_icon_init(static_cast<GQvCellRendererIcon *>(data));
127 gqv_cell_renderer_icon_init(GQvCellRendererIcon *cellicon)
129 g_object_set(G_OBJECT(cellicon), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
130 gtk_cell_renderer_set_padding(GTK_CELL_RENDERER(cellicon), 2, 2);
134 gqv_cell_renderer_icon_class_init_wrapper(void *data, void *)
136 gqv_cell_renderer_icon_class_init(static_cast<GQvCellRendererIconClass *>(data));
140 gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *icon_class)
142 GObjectClass *object_class = G_OBJECT_CLASS(icon_class);
143 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(icon_class);
145 parent_class = g_type_class_peek_parent(icon_class);
147 object_class->finalize = gqv_cell_renderer_icon_finalize;
149 object_class->get_property = gqv_cell_renderer_icon_get_property;
150 object_class->set_property = gqv_cell_renderer_icon_set_property;
152 cell_class->get_size = gqv_cell_renderer_icon_get_size;
153 cell_class->render = gqv_cell_renderer_icon_render;
154 cell_class->activate = gqv_cell_renderer_icon_activate;
156 g_object_class_install_property(object_class,
158 g_param_spec_object("pixbuf",
160 "The pixbuf to render",
164 g_object_class_install_property(object_class,
166 g_param_spec_string("text",
172 g_object_class_install_property(object_class,
174 g_param_spec_boxed("background_rgba",
176 "Background color as a GdkRGBA",
180 g_object_class_install_property(object_class,
182 g_param_spec_boxed("foreground_rgba",
184 "Foreground color as a GdkRGBA",
188 g_object_class_install_property(object_class,
190 g_param_spec_boolean("has_focus",
192 "Draw focus indicator",
196 g_object_class_install_property(object_class,
198 g_param_spec_int("fixed_width",
201 -1, FIXED_ICON_SIZE_MAX,
205 g_object_class_install_property(object_class,
207 g_param_spec_int("fixed_height",
209 "Height of icon excluding text",
210 -1, FIXED_ICON_SIZE_MAX,
214 g_object_class_install_property(object_class,
216 g_param_spec_boolean("background_set",
218 "Whether this tag affects the background color",
222 g_object_class_install_property(object_class,
224 g_param_spec_boolean("foreground_set",
226 "Whether this tag affects the foreground color",
230 g_object_class_install_property(object_class,
232 g_param_spec_boolean("show_text",
234 "Whether the text is displayed",
238 g_object_class_install_property(object_class,
240 g_param_spec_boolean("show_marks",
242 "Whether the marks are displayed",
246 g_object_class_install_property(object_class,
248 g_param_spec_int("num_marks",
255 g_object_class_install_property(object_class,
257 g_param_spec_uint("marks",
264 g_object_class_install_property(object_class,
266 g_param_spec_uint("toggled_mark",
272 toggle_cell_signals[TOGGLED] =
273 g_signal_new("toggled",
274 G_OBJECT_CLASS_TYPE (object_class),
276 G_STRUCT_OFFSET (GQvCellRendererIconClass, toggled),
278 g_cclosure_marshal_VOID__STRING,
285 gqv_cell_renderer_icon_finalize(GObject *object)
287 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
289 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
291 g_free(cellicon->text);
293 (*(G_OBJECT_CLASS(parent_class))->finalize)(object);
297 gqv_cell_renderer_icon_get_property(GObject *object,
302 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
307 g_value_set_object(value, cellicon->pixbuf ? G_OBJECT(cellicon->pixbuf) : nullptr);
310 g_value_set_string(value, cellicon->text);
312 case PROP_BACKGROUND_GDK:
316 color.red = cellicon->background.red / 65535.0;
317 color.green = cellicon->background.green / 65535.0;
318 color.blue = cellicon->background.blue / 65535.0;
320 g_value_set_boxed(value, &color);
323 case PROP_FOREGROUND_GDK:
327 color.red = cellicon->foreground.red / 65535.0;
328 color.green = cellicon->foreground.green / 65535.0;
329 color.blue = cellicon->foreground.blue / 65535.0;
331 g_value_set_boxed(value, &color);
335 g_value_set_boolean(value, cellicon->focused);
337 case PROP_FIXED_WIDTH:
338 g_value_set_int(value, cellicon->fixed_width);
340 case PROP_FIXED_HEIGHT:
341 g_value_set_int(value, cellicon->fixed_height);
343 case PROP_BACKGROUND_SET:
344 g_value_set_boolean(value, cellicon->background_set);
346 case PROP_FOREGROUND_SET:
347 g_value_set_boolean(value, cellicon->foreground_set);
350 g_value_set_boolean(value, cellicon->show_text);
352 case PROP_SHOW_MARKS:
353 g_value_set_boolean(value, cellicon->show_marks);
356 g_value_set_int(value, cellicon->num_marks);
359 g_value_set_uint(value, cellicon->marks);
362 g_value_set_uint(value, cellicon->toggled_mark);
365 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
370 static void set_bg_color(GQvCellRendererIcon *cellicon, GdkRGBA *color)
374 if (!cellicon->background_set)
376 cellicon->background_set = TRUE;
377 g_object_notify(G_OBJECT(cellicon), "background_set");
380 cellicon->background.red = color->red * 65535;
381 cellicon->background.green = color->green * 65535;
382 cellicon->background.blue = color->blue * 65535;
386 if (cellicon->background_set)
388 cellicon->background_set = FALSE;
389 g_object_notify(G_OBJECT(cellicon), "background_set");
394 static void set_fg_color(GQvCellRendererIcon *cellicon, GdkRGBA *color)
398 if (!cellicon->foreground_set)
400 cellicon->foreground_set = TRUE;
401 g_object_notify(G_OBJECT(cellicon), "foreground_set");
403 cellicon->foreground.red = color->red * 65535;
404 cellicon->foreground.green = color->green * 65535;
405 cellicon->foreground.blue = color->blue * 65535;
409 if (cellicon->foreground_set)
411 cellicon->foreground_set = FALSE;
412 g_object_notify(G_OBJECT(cellicon), "foreground_set");
418 gqv_cell_renderer_icon_set_property(GObject *object,
423 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
431 pixbuf = static_cast<GdkPixbuf *>(g_value_get_object(value));
432 if (pixbuf) g_object_ref(pixbuf);
433 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
434 cellicon->pixbuf = pixbuf;
441 text = cellicon->text;
442 cellicon->text = g_strdup(g_value_get_string(value));
445 g_object_notify(object, "text");
448 case PROP_BACKGROUND_GDK:
449 set_bg_color(cellicon, static_cast<GdkRGBA *>(g_value_get_boxed(value)));
451 case PROP_FOREGROUND_GDK:
452 set_fg_color(cellicon, static_cast<GdkRGBA *>(g_value_get_boxed(value)));
455 cellicon->focused = g_value_get_boolean(value);
457 case PROP_FIXED_WIDTH:
458 cellicon->fixed_width = g_value_get_int(value);
460 case PROP_FIXED_HEIGHT:
461 cellicon->fixed_height = g_value_get_int(value);
463 case PROP_BACKGROUND_SET:
464 cellicon->background_set = g_value_get_boolean(value);
466 case PROP_FOREGROUND_SET:
467 cellicon->foreground_set = g_value_get_boolean(value);
470 cellicon->show_text = g_value_get_boolean(value);
472 case PROP_SHOW_MARKS:
473 cellicon->show_marks = g_value_get_boolean(value);
476 cellicon->num_marks = g_value_get_int(value);
479 cellicon->marks = g_value_get_uint(value);
482 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
488 gqv_cell_renderer_icon_get_layout(GQvCellRendererIcon *cellicon, GtkWidget *widget, gboolean will_render)
493 width = (cellicon->fixed_width > 0) ? cellicon->fixed_width * PANGO_SCALE : -1;
495 layout = gtk_widget_create_pango_layout(widget, cellicon->text);
496 pango_layout_set_width(layout, width);
497 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
498 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
502 PangoAttrList *attr_list;
504 attr_list = pango_attr_list_new();
506 if (cellicon->foreground_set)
509 PangoAttribute *attr;
511 color = cellicon->foreground;
513 attr = pango_attr_foreground_new(color.red, color.green, color.blue);
515 attr->start_index = 0;
516 attr->end_index = G_MAXINT;
517 pango_attr_list_insert(attr_list, attr);
520 pango_layout_set_attributes(layout, attr_list);
521 pango_attr_list_unref(attr_list);
528 * gqv_cell_renderer_icon_new:
530 * Creates a new #GQvCellRendererIcon. Adjust rendering
531 * parameters using object properties. Object properties can be set
532 * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
533 * can bind a property to a value in a #GtkTreeModel. For example, you
534 * can bind the "pixbuf" property on the cell renderer to a pixbuf value
535 * in the model, thus rendering a different image in each row of the
538 * Return value: the new cell renderer
541 gqv_cell_renderer_icon_new()
543 return static_cast<GtkCellRenderer *>(g_object_new(GQV_TYPE_CELL_RENDERER_ICON, nullptr));
546 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
548 const GdkRectangle *cell_area,
554 auto cellicon = reinterpret_cast<GQvCellRendererIcon *>(cell);
558 gfloat xalign, yalign;
560 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
561 gtk_cell_renderer_get_alignment(cell, &xalign, &yalign);
563 if (cellicon->fixed_width > 0)
565 calc_width = cellicon->fixed_width;
569 calc_width = (cellicon->pixbuf) ? gdk_pixbuf_get_width(cellicon->pixbuf) : 0;
572 if (cellicon->fixed_height > 0)
574 calc_height = cellicon->fixed_height;
578 calc_height = (cellicon->pixbuf) ? gdk_pixbuf_get_height(cellicon->pixbuf) : 0;
581 if (cellicon->show_text && cellicon->text)
586 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, FALSE);
587 pango_layout_get_pixel_extents(layout, nullptr, &rect);
588 g_object_unref(layout);
590 calc_width = MAX(calc_width, rect.width);
591 calc_height += rect.height;
594 if (cellicon->show_marks)
596 calc_height += TOGGLE_SPACING;
597 calc_width = MAX(calc_width, TOGGLE_SPACING * cellicon->num_marks);
600 calc_width += xpad * 2;
601 calc_height += ypad * 2;
603 if (x_offset) *x_offset = 0;
604 if (y_offset) *y_offset = 0;
606 if (cell_area && calc_width > 0 && calc_height > 0)
610 *x_offset = (xalign * (cell_area->width - calc_width - 2 * xpad));
611 *x_offset = MAX(*x_offset, 0) + xpad;
615 *y_offset = (yalign * (cell_area->height - calc_height - 2 * ypad));
616 *y_offset = MAX(*y_offset, 0) + ypad;
620 if (width) *width = calc_width;
621 if (height) *height = calc_height;
624 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
627 const GdkRectangle *,
628 const GdkRectangle *cell_area,
629 GtkCellRendererState flags)
632 GtkStyleContext *context = gtk_widget_get_style_context(widget);
633 auto cellicon = reinterpret_cast<GQvCellRendererIcon *>(cell);
636 GdkRectangle cell_rect;
641 pixbuf = cellicon->pixbuf;
642 text = cellicon->text;
643 if (!pixbuf && !text)
648 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
650 gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
651 &cell_rect.x, &cell_rect.y,
652 &cell_rect.width, &cell_rect.height);
656 cell_rect.width -= xpad * 2;
657 cell_rect.height -= ypad * 2;
659 if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
661 if (gtk_widget_has_focus(widget))
662 state = GTK_STATE_FLAG_SELECTED;
664 state = GTK_STATE_FLAG_ACTIVE;
668 if (!gtk_widget_is_sensitive(widget))
669 state = GTK_STATE_FLAG_INSENSITIVE;
671 state = GTK_STATE_FLAG_NORMAL;
673 gtk_style_context_set_state(context, state);
677 GdkRectangle pix_rect;
678 GdkRectangle draw_rect;
680 pix_rect.width = gdk_pixbuf_get_width(pixbuf);
681 pix_rect.height = gdk_pixbuf_get_height(pixbuf);
683 pix_rect.x = cell_area->x + (cell_area->width - pix_rect.width) / 2;
685 if (cellicon->fixed_height > 0)
687 pix_rect.y = cell_area->y + ypad + (cellicon->fixed_height - pix_rect.height) / 2;
691 pix_rect.y = cell_area->y + cell_rect.y;
694 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect))
696 gdk_cairo_set_source_pixbuf(cr, pixbuf, pix_rect.x, pix_rect.y);
707 if (cellicon->show_text && text)
710 PangoRectangle text_rect;
711 GdkRectangle pix_rect;
712 GdkRectangle draw_rect;
713 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, TRUE);
714 pango_layout_get_pixel_extents(layout, nullptr, &text_rect);
716 pix_rect.width = text_rect.width;
717 pix_rect.height = text_rect.height;
718 pix_rect.x = cell_area->x + xpad + (cell_rect.width - text_rect.width + 1) / 2;
719 pix_rect.y = cell_area->y + ypad + (cell_rect.height - text_rect.height);
721 if (cellicon->show_marks)
723 pix_rect.y -= TOGGLE_SPACING;
726 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect))
728 gtk_render_layout(context, cr, pix_rect.x - text_rect.x, pix_rect.y, layout);
730 g_object_unref(layout);
733 if (cellicon->show_marks)
735 GdkRectangle pix_rect;
736 GdkRectangle draw_rect;
739 pix_rect.width = TOGGLE_SPACING * cellicon->num_marks;
740 pix_rect.height = TOGGLE_SPACING;
741 pix_rect.x = cell_area->x + xpad + (cell_rect.width - pix_rect.width + 1) / 2 + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
742 pix_rect.y = cell_area->y + ypad + (cell_rect.height - pix_rect.height) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
744 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect))
746 for (i = 0; i < cellicon->num_marks; i++)
748 state = static_cast<GtkStateFlags>(state & ~GTK_STATE_FLAG_CHECKED);
750 if ((cellicon->marks & (1 << i)))
751 state = static_cast<GtkStateFlags>(state | GTK_STATE_FLAG_CHECKED);
755 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2.0,
757 TOGGLE_WIDTH, TOGGLE_WIDTH);
760 gtk_style_context_save(context);
761 gtk_style_context_set_state(context, state);
763 gtk_style_context_add_class(context, GTK_STYLE_CLASS_CHECK);
765 gtk_style_context_add_class(context, "marks");
767 if (state & GTK_STATE_FLAG_CHECKED)
769 gtk_render_check(context, cr,
770 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2.0,
772 TOGGLE_WIDTH, TOGGLE_WIDTH);
774 gtk_render_frame(context, cr,
775 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2.0,
777 TOGGLE_WIDTH, TOGGLE_WIDTH);
779 if (cellicon->focused && gtk_widget_has_focus(widget))
781 gtk_render_focus(context, cr,
782 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2.0,
783 pix_rect.y, TOGGLE_WIDTH, TOGGLE_WIDTH);
785 gtk_style_context_restore(context);
792 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
796 const GdkRectangle *,
797 const GdkRectangle *cell_area,
798 GtkCellRendererState)
800 auto cellicon = reinterpret_cast<GQvCellRendererIcon *>(cell);
801 GdkEventButton *bevent = &event->button;
803 if (cellicon->show_marks &&
804 event->type == GDK_BUTTON_PRESS &&
805 !(bevent->state & GDK_SHIFT_MASK ) &&
806 !(bevent->state & GDK_CONTROL_MASK ))
809 GdkRectangle cell_rect;
813 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
815 gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
816 &cell_rect.x, &cell_rect.y,
817 &cell_rect.width, &cell_rect.height);
821 cell_rect.width -= xpad * 2;
822 cell_rect.height -= ypad * 2;
824 rect.width = TOGGLE_WIDTH;
825 rect.height = TOGGLE_WIDTH;
826 rect.y = cell_area->y + ypad + (cell_rect.height - TOGGLE_SPACING) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
827 for (i = 0; i < cellicon->num_marks; i++)
829 rect.x = cell_area->x + xpad + (cell_rect.width - TOGGLE_SPACING * cellicon->num_marks + 1) / 2 + i * TOGGLE_SPACING;
831 if (bevent->x >= rect.x && bevent->x < rect.x + rect.width &&
832 bevent->y >= rect.y && bevent->y < rect.y + rect.height)
834 cellicon->toggled_mark = i;
835 g_signal_emit(cell, toggle_cell_signals[TOGGLED], 0, path);
842 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */