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"
23 #define FIXED_ICON_SIZE_MAX 512
26 static void gqv_cell_renderer_icon_get_property(GObject *object,
30 static void gqv_cell_renderer_icon_set_property(GObject *object,
34 static void gqv_cell_renderer_icon_init_wrapper(void *, void *);
35 static void gqv_cell_renderer_icon_init(GQvCellRendererIcon *celltext);
36 static void gqv_cell_renderer_icon_class_init_wrapper(void *, void *);
37 static void gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *icon_class);
38 static void gqv_cell_renderer_icon_finalize(GObject *object);
39 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
41 const GdkRectangle *rectangle,
47 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
50 const GdkRectangle *background_area,
51 const GdkRectangle *cell_area,
52 GtkCellRendererState flags);
54 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
58 const GdkRectangle *background_area,
59 const GdkRectangle *cell_area,
60 GtkCellRendererState flags);
85 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
87 static gpointer parent_class;
90 gqv_cell_renderer_icon_get_type(void)
92 static GType cell_icon_type = 0;
96 static const GTypeInfo cell_icon_info =
98 sizeof(GQvCellRendererIconClass), /* class_size */
100 NULL, /* base_finalize */
101 (GClassInitFunc) gqv_cell_renderer_icon_class_init_wrapper, /* class_init */
102 NULL, /* class_finalize */
103 NULL, /* class_data */
104 sizeof(GQvCellRendererIcon), /* instance_size */
106 (GInstanceInitFunc) gqv_cell_renderer_icon_init_wrapper, /* instance_init */
107 NULL, /* value_table */
110 cell_icon_type = g_type_register_static(GTK_TYPE_CELL_RENDERER,
111 "GQvCellRendererIcon",
112 &cell_icon_info, GTypeFlags(0));
115 return cell_icon_type;
119 gqv_cell_renderer_icon_init_wrapper(void *data, void *UNUSED(user_data))
121 gqv_cell_renderer_icon_init(static_cast<GQvCellRendererIcon *>(data));
125 gqv_cell_renderer_icon_init(GQvCellRendererIcon *cellicon)
127 g_object_set(G_OBJECT(cellicon), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
128 gtk_cell_renderer_set_padding(GTK_CELL_RENDERER(cellicon), 2, 2);
132 gqv_cell_renderer_icon_class_init_wrapper(void *data, void *UNUSED(user_data))
134 gqv_cell_renderer_icon_class_init(static_cast<GQvCellRendererIconClass *>(data));
138 gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *icon_class)
140 GObjectClass *object_class = G_OBJECT_CLASS(icon_class);
141 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(icon_class);
143 parent_class = g_type_class_peek_parent(icon_class);
145 object_class->finalize = gqv_cell_renderer_icon_finalize;
147 object_class->get_property = gqv_cell_renderer_icon_get_property;
148 object_class->set_property = gqv_cell_renderer_icon_set_property;
150 cell_class->get_size = gqv_cell_renderer_icon_get_size;
151 cell_class->render = gqv_cell_renderer_icon_render;
152 cell_class->activate = gqv_cell_renderer_icon_activate;
154 g_object_class_install_property(object_class,
156 g_param_spec_object("pixbuf",
158 "The pixbuf to render",
162 g_object_class_install_property(object_class,
164 g_param_spec_string("text",
170 g_object_class_install_property(object_class,
172 g_param_spec_boxed("background_gdk",
174 "Background color as a GdkColor",
178 g_object_class_install_property(object_class,
180 g_param_spec_boxed("foreground_gdk",
182 "Foreground color as a GdkColor",
186 g_object_class_install_property(object_class,
188 g_param_spec_boolean("has_focus",
190 "Draw focus indicator",
194 g_object_class_install_property(object_class,
196 g_param_spec_int("fixed_width",
199 -1, FIXED_ICON_SIZE_MAX,
203 g_object_class_install_property(object_class,
205 g_param_spec_int("fixed_height",
207 "Height of icon excluding text",
208 -1, FIXED_ICON_SIZE_MAX,
212 g_object_class_install_property(object_class,
214 g_param_spec_boolean("background_set",
216 "Whether this tag affects the background color",
220 g_object_class_install_property(object_class,
222 g_param_spec_boolean("foreground_set",
224 "Whether this tag affects the foreground color",
228 g_object_class_install_property(object_class,
230 g_param_spec_boolean("show_text",
232 "Whether the text is displayed",
236 g_object_class_install_property(object_class,
238 g_param_spec_boolean("show_marks",
240 "Whether the marks are displayed",
244 g_object_class_install_property(object_class,
246 g_param_spec_int("num_marks",
253 g_object_class_install_property(object_class,
255 g_param_spec_uint("marks",
262 g_object_class_install_property(object_class,
264 g_param_spec_uint("toggled_mark",
270 toggle_cell_signals[TOGGLED] =
271 g_signal_new("toggled",
272 G_OBJECT_CLASS_TYPE (object_class),
274 G_STRUCT_OFFSET (GQvCellRendererIconClass, toggled),
276 g_cclosure_marshal_VOID__STRING,
283 gqv_cell_renderer_icon_finalize(GObject *object)
285 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
287 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
289 g_free(cellicon->text);
291 (*(G_OBJECT_CLASS(parent_class))->finalize)(object);
295 gqv_cell_renderer_icon_get_property(GObject *object,
300 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
305 g_value_set_object(value, cellicon->pixbuf ? G_OBJECT(cellicon->pixbuf) : NULL);
308 g_value_set_string(value, cellicon->text);
310 case PROP_BACKGROUND_GDK:
314 color.red = cellicon->background.red;
315 color.green = cellicon->background.green;
316 color.blue = cellicon->background.blue;
318 g_value_set_boxed(value, &color);
321 case PROP_FOREGROUND_GDK:
325 color.red = cellicon->foreground.red;
326 color.green = cellicon->foreground.green;
327 color.blue = cellicon->foreground.blue;
329 g_value_set_boxed(value, &color);
333 g_value_set_boolean(value, cellicon->focused);
335 case PROP_FIXED_WIDTH:
336 g_value_set_int(value, cellicon->fixed_width);
338 case PROP_FIXED_HEIGHT:
339 g_value_set_int(value, cellicon->fixed_height);
341 case PROP_BACKGROUND_SET:
342 g_value_set_boolean(value, cellicon->background_set);
344 case PROP_FOREGROUND_SET:
345 g_value_set_boolean(value, cellicon->foreground_set);
348 g_value_set_boolean(value, cellicon->show_text);
350 case PROP_SHOW_MARKS:
351 g_value_set_boolean(value, cellicon->show_marks);
354 g_value_set_int(value, cellicon->num_marks);
357 g_value_set_uint(value, cellicon->marks);
360 g_value_set_uint(value, cellicon->toggled_mark);
363 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
369 set_bg_color(GQvCellRendererIcon *cellicon,
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;
381 cellicon->background.green = color->green;
382 cellicon->background.blue = color->blue;
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,
399 if (!cellicon->foreground_set)
401 cellicon->foreground_set = TRUE;
402 g_object_notify(G_OBJECT(cellicon), "foreground_set");
405 cellicon->foreground.red = color->red;
406 cellicon->foreground.green = color->green;
407 cellicon->foreground.blue = color->blue;
411 if (cellicon->foreground_set)
413 cellicon->foreground_set = FALSE;
414 g_object_notify(G_OBJECT(cellicon), "foreground_set");
420 gqv_cell_renderer_icon_set_property(GObject *object,
425 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
433 pixbuf = (GdkPixbuf *) g_value_get_object(value);
434 if (pixbuf) g_object_ref(pixbuf);
435 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
436 cellicon->pixbuf = pixbuf;
443 text = cellicon->text;
444 cellicon->text = g_strdup(g_value_get_string(value));
447 g_object_notify(object, "text");
450 case PROP_BACKGROUND_GDK:
451 set_bg_color(cellicon, static_cast<GdkColor *>(g_value_get_boxed(value)));
453 case PROP_FOREGROUND_GDK:
454 set_fg_color(cellicon, static_cast<GdkColor *>(g_value_get_boxed(value)));
457 cellicon->focused = g_value_get_boolean(value);
459 case PROP_FIXED_WIDTH:
460 cellicon->fixed_width = g_value_get_int(value);
462 case PROP_FIXED_HEIGHT:
463 cellicon->fixed_height = g_value_get_int(value);
465 case PROP_BACKGROUND_SET:
466 cellicon->background_set = g_value_get_boolean(value);
468 case PROP_FOREGROUND_SET:
469 cellicon->foreground_set = g_value_get_boolean(value);
472 cellicon->show_text = g_value_get_boolean(value);
474 case PROP_SHOW_MARKS:
475 cellicon->show_marks = g_value_get_boolean(value);
478 cellicon->num_marks = g_value_get_int(value);
481 cellicon->marks = g_value_get_uint(value);
484 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
490 gqv_cell_renderer_icon_get_layout(GQvCellRendererIcon *cellicon, GtkWidget *widget, gboolean will_render)
495 width = (cellicon->fixed_width > 0) ? cellicon->fixed_width * PANGO_SCALE : -1;
497 layout = gtk_widget_create_pango_layout(widget, cellicon->text);
498 pango_layout_set_width(layout, width);
499 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
500 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
504 PangoAttrList *attr_list;
506 attr_list = pango_attr_list_new();
508 if (cellicon->foreground_set)
511 PangoAttribute *attr;
513 color = cellicon->foreground;
515 attr = pango_attr_foreground_new(color.red, color.green, color.blue);
517 attr->start_index = 0;
518 attr->end_index = G_MAXINT;
519 pango_attr_list_insert(attr_list, attr);
522 pango_layout_set_attributes(layout, attr_list);
523 pango_attr_list_unref(attr_list);
530 * gqv_cell_renderer_icon_new:
532 * Creates a new #GQvCellRendererIcon. Adjust rendering
533 * parameters using object properties. Object properties can be set
534 * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
535 * can bind a property to a value in a #GtkTreeModel. For example, you
536 * can bind the "pixbuf" property on the cell renderer to a pixbuf value
537 * in the model, thus rendering a different image in each row of the
540 * Return value: the new cell renderer
543 gqv_cell_renderer_icon_new(void)
545 return static_cast<GtkCellRenderer *>(g_object_new(GQV_TYPE_CELL_RENDERER_ICON, NULL));
548 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
550 const GdkRectangle *cell_area,
556 GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
560 gfloat xalign, yalign;
562 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
563 gtk_cell_renderer_get_alignment(cell, &xalign, &yalign);
565 if (cellicon->fixed_width > 0)
567 calc_width = cellicon->fixed_width;
571 calc_width = (cellicon->pixbuf) ? gdk_pixbuf_get_width(cellicon->pixbuf) : 0;
574 if (cellicon->fixed_height > 0)
576 calc_height = cellicon->fixed_height;
580 calc_height = (cellicon->pixbuf) ? gdk_pixbuf_get_height(cellicon->pixbuf) : 0;
583 if (cellicon->show_text && cellicon->text)
588 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, FALSE);
589 pango_layout_get_pixel_extents(layout, NULL, &rect);
590 g_object_unref(layout);
592 calc_width = MAX(calc_width, rect.width);
593 calc_height += rect.height;
596 if (cellicon->show_marks)
598 calc_height += TOGGLE_SPACING;
599 calc_width = MAX(calc_width, TOGGLE_SPACING * cellicon->num_marks);
602 calc_width += xpad * 2;
603 calc_height += ypad * 2;
605 if (x_offset) *x_offset = 0;
606 if (y_offset) *y_offset = 0;
608 if (cell_area && calc_width > 0 && calc_height > 0)
612 *x_offset = (xalign * (cell_area->width - calc_width - 2 * xpad));
613 *x_offset = MAX(*x_offset, 0) + xpad;
617 *y_offset = (yalign * (cell_area->height - calc_height - 2 * ypad));
618 *y_offset = MAX(*y_offset, 0) + ypad;
622 if (width) *width = calc_width;
623 if (height) *height = calc_height;
626 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
629 const GdkRectangle *UNUSED(background_area),
630 const GdkRectangle *cell_area,
631 GtkCellRendererState flags)
634 GtkStyleContext *context = gtk_widget_get_style_context(widget);
635 GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
638 GdkRectangle cell_rect;
643 pixbuf = cellicon->pixbuf;
644 text = cellicon->text;
645 if (!pixbuf && !text)
650 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
652 gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
653 &cell_rect.x, &cell_rect.y,
654 &cell_rect.width, &cell_rect.height);
658 cell_rect.width -= xpad * 2;
659 cell_rect.height -= ypad * 2;
661 if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
663 if (gtk_widget_has_focus(widget))
664 state = GTK_STATE_FLAG_SELECTED;
666 state = GTK_STATE_FLAG_ACTIVE;
670 if (gtk_widget_get_state(widget) == GTK_STATE_INSENSITIVE)
671 state = GTK_STATE_FLAG_INSENSITIVE;
673 state = GTK_STATE_FLAG_NORMAL;
675 gtk_style_context_set_state(context, state);
679 GdkRectangle pix_rect;
680 GdkRectangle draw_rect;
682 pix_rect.width = gdk_pixbuf_get_width(pixbuf);
683 pix_rect.height = gdk_pixbuf_get_height(pixbuf);
685 pix_rect.x = cell_area->x + (cell_area->width - pix_rect.width) / 2;
687 if (cellicon->fixed_height > 0)
689 pix_rect.y = cell_area->y + ypad + (cellicon->fixed_height - pix_rect.height) / 2;
693 pix_rect.y = cell_area->y + cell_rect.y;
696 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect))
698 gdk_cairo_set_source_pixbuf(cr, pixbuf, pix_rect.x, pix_rect.y);
709 if (cellicon->show_text && text)
712 PangoRectangle text_rect;
713 GdkRectangle pix_rect;
714 GdkRectangle draw_rect;
715 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, TRUE);
716 pango_layout_get_pixel_extents(layout, NULL, &text_rect);
718 pix_rect.width = text_rect.width;
719 pix_rect.height = text_rect.height;
720 pix_rect.x = cell_area->x + xpad + (cell_rect.width - text_rect.width + 1) / 2;
721 pix_rect.y = cell_area->y + ypad + (cell_rect.height - text_rect.height);
723 if (cellicon->show_marks)
725 pix_rect.y -= TOGGLE_SPACING;
728 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect))
730 gtk_render_layout(context, cr, pix_rect.x - text_rect.x, pix_rect.y, layout);
732 g_object_unref(layout);
735 if (cellicon->show_marks)
737 GdkRectangle pix_rect;
738 GdkRectangle draw_rect;
741 pix_rect.width = TOGGLE_SPACING * cellicon->num_marks;
742 pix_rect.height = TOGGLE_SPACING;
743 pix_rect.x = cell_area->x + xpad + (cell_rect.width - pix_rect.width + 1) / 2 + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
744 pix_rect.y = cell_area->y + ypad + (cell_rect.height - pix_rect.height) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
746 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect))
748 for (i = 0; i < cellicon->num_marks; i++)
750 state = static_cast<GtkStateFlags>(state & ~GTK_STATE_FLAG_CHECKED);
752 if ((cellicon->marks & (1 << i)))
753 state = static_cast<GtkStateFlags>(state | GTK_STATE_FLAG_CHECKED);
757 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
759 TOGGLE_WIDTH, TOGGLE_WIDTH);
762 gtk_style_context_save(context);
763 gtk_style_context_set_state(context, state);
765 gtk_style_context_add_class(context, GTK_STYLE_CLASS_CHECK);
767 gtk_style_context_add_class(context, "marks");
768 GtkStyleProvider *provider;
769 provider = (GtkStyleProvider *)gtk_css_provider_new();
770 gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(provider),
772 "border-color: #808080;\n"
773 "border-style: solid;\n"
774 "border-width: 1px;\n"
775 "border-radius: 0px;\n"
778 gtk_style_context_add_provider(context, provider,
779 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
781 if (state & GTK_STATE_FLAG_CHECKED)
783 gtk_render_check(context, cr,
784 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
786 TOGGLE_WIDTH, TOGGLE_WIDTH);
788 gtk_render_frame(context, cr,
789 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
791 TOGGLE_WIDTH, TOGGLE_WIDTH);
793 if (cellicon->focused && gtk_widget_has_focus(widget))
795 gtk_render_focus(context, cr,
796 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
797 pix_rect.y, TOGGLE_WIDTH, TOGGLE_WIDTH);
799 gtk_style_context_restore(context);
801 gtk_style_context_remove_provider(context, provider);
802 g_object_unref(provider);
808 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
812 const GdkRectangle *UNUSED(background_area),
813 const GdkRectangle *cell_area,
814 GtkCellRendererState UNUSED(flags))
816 GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
817 GdkEventButton *bevent = &event->button;
819 if (cellicon->show_marks &&
820 event->type == GDK_BUTTON_PRESS &&
821 !(bevent->state & GDK_SHIFT_MASK ) &&
822 !(bevent->state & GDK_CONTROL_MASK ))
825 GdkRectangle cell_rect;
829 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
831 gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
832 &cell_rect.x, &cell_rect.y,
833 &cell_rect.width, &cell_rect.height);
837 cell_rect.width -= xpad * 2;
838 cell_rect.height -= ypad * 2;
840 rect.width = TOGGLE_WIDTH;
841 rect.height = TOGGLE_WIDTH;
842 rect.y = cell_area->y + ypad + (cell_rect.height - TOGGLE_SPACING) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
843 for (i = 0; i < cellicon->num_marks; i++)
845 rect.x = cell_area->x + xpad + (cell_rect.width - TOGGLE_SPACING * cellicon->num_marks + 1) / 2 + i * TOGGLE_SPACING;
847 if (bevent->x >= rect.x && bevent->x < rect.x + rect.width &&
848 bevent->y >= rect.y && bevent->y < rect.y + rect.height)
850 cellicon->toggled_mark = i;
851 g_signal_emit(cell, toggle_cell_signals[TOGGLED], 0, path);
858 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */