1 /* cellrenderericon.c, based on:
3 * gtkcellrendererpixbuf.c
4 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 #include <gtk/gtk.h> /* To define GTK_CHECK_VERSION */
24 #include "cellrenderericon.h"
28 #define FIXED_ICON_SIZE_MAX 512
30 #define TOGGLE_WIDTH 13
31 #define TOGGLE_SPACING 18
34 static void gqv_cell_renderer_icon_get_property(GObject *object,
38 static void gqv_cell_renderer_icon_set_property(GObject *object,
42 static void gqv_cell_renderer_icon_init(GQvCellRendererIcon *celltext);
43 static void gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *class);
44 static void gqv_cell_renderer_icon_finalize(GObject *object);
45 #if GTK_CHECK_VERSION(3,0,0)
46 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
48 const GdkRectangle *rectangle,
54 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
56 GdkRectangle *rectangle,
62 #if GTK_CHECK_VERSION(3,0,0)
63 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
66 const GdkRectangle *background_area,
67 const GdkRectangle *cell_area,
68 GtkCellRendererState flags);
70 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
73 GdkRectangle *background_area,
74 GdkRectangle *cell_area,
75 GdkRectangle *expose_area,
76 GtkCellRendererState flags);
80 #if GTK_CHECK_VERSION(3,0,0)
81 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
85 const GdkRectangle *background_area,
86 const GdkRectangle *cell_area,
87 GtkCellRendererState flags);
89 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
93 GdkRectangle *background_area,
94 GdkRectangle *cell_area,
95 GtkCellRendererState flags);
122 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
124 static gpointer parent_class;
127 gqv_cell_renderer_icon_get_type(void)
129 static GType cell_icon_type = 0;
133 static const GTypeInfo cell_icon_info =
135 sizeof(GQvCellRendererIconClass), /* class_size */
136 NULL, /* base_init */
137 NULL, /* base_finalize */
138 (GClassInitFunc) gqv_cell_renderer_icon_class_init, /* class_init */
139 NULL, /* class_finalize */
140 NULL, /* class_data */
141 sizeof(GQvCellRendererIcon), /* instance_size */
143 (GInstanceInitFunc) gqv_cell_renderer_icon_init, /* instance_init */
144 NULL, /* value_table */
147 cell_icon_type = g_type_register_static(GTK_TYPE_CELL_RENDERER,
148 "GQvCellRendererIcon",
152 return cell_icon_type;
156 gqv_cell_renderer_icon_init(GQvCellRendererIcon *cellicon)
158 g_object_set(G_OBJECT(cellicon), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
159 gtk_cell_renderer_set_padding(GTK_CELL_RENDERER(cellicon), 2, 2);
163 gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *class)
165 GObjectClass *object_class = G_OBJECT_CLASS(class);
166 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(class);
168 parent_class = g_type_class_peek_parent(class);
170 object_class->finalize = gqv_cell_renderer_icon_finalize;
172 object_class->get_property = gqv_cell_renderer_icon_get_property;
173 object_class->set_property = gqv_cell_renderer_icon_set_property;
175 cell_class->get_size = gqv_cell_renderer_icon_get_size;
176 cell_class->render = gqv_cell_renderer_icon_render;
177 cell_class->activate = gqv_cell_renderer_icon_activate;
179 g_object_class_install_property(object_class,
181 g_param_spec_object("pixbuf",
183 "The pixbuf to render",
187 g_object_class_install_property(object_class,
189 g_param_spec_string("text",
195 g_object_class_install_property(object_class,
197 g_param_spec_boxed("background_gdk",
199 "Background color as a GdkColor",
203 g_object_class_install_property(object_class,
205 g_param_spec_boxed("foreground_gdk",
207 "Foreground color as a GdkColor",
211 g_object_class_install_property(object_class,
213 g_param_spec_boolean("has_focus",
215 "Draw focus indicator",
219 g_object_class_install_property(object_class,
221 g_param_spec_int("fixed_width",
224 -1, FIXED_ICON_SIZE_MAX,
228 g_object_class_install_property(object_class,
230 g_param_spec_int("fixed_height",
232 "Height of icon excluding text",
233 -1, FIXED_ICON_SIZE_MAX,
237 g_object_class_install_property(object_class,
239 g_param_spec_boolean("background_set",
241 "Whether this tag affects the background color",
245 g_object_class_install_property(object_class,
247 g_param_spec_boolean("foreground_set",
249 "Whether this tag affects the foreground color",
253 g_object_class_install_property(object_class,
255 g_param_spec_boolean("show_text",
257 "Whether the text is displayed",
261 g_object_class_install_property(object_class,
263 g_param_spec_boolean("show_marks",
265 "Whether the marks are displayed",
269 g_object_class_install_property(object_class,
271 g_param_spec_int("num_marks",
278 g_object_class_install_property(object_class,
280 g_param_spec_uint("marks",
287 g_object_class_install_property(object_class,
289 g_param_spec_uint("toggled_mark",
295 toggle_cell_signals[TOGGLED] =
296 g_signal_new("toggled",
297 G_OBJECT_CLASS_TYPE (object_class),
299 G_STRUCT_OFFSET (GQvCellRendererIconClass, toggled),
301 g_cclosure_marshal_VOID__STRING,
308 gqv_cell_renderer_icon_finalize(GObject *object)
310 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
312 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
314 g_free(cellicon->text);
316 (*(G_OBJECT_CLASS(parent_class))->finalize)(object);
320 gqv_cell_renderer_icon_get_property(GObject *object,
325 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
330 g_value_set_object(value, cellicon->pixbuf ? G_OBJECT(cellicon->pixbuf) : NULL);
333 g_value_set_string(value, cellicon->text);
335 case PROP_BACKGROUND_GDK:
339 color.red = cellicon->background.red;
340 color.green = cellicon->background.green;
341 color.blue = cellicon->background.blue;
343 g_value_set_boxed(value, &color);
346 case PROP_FOREGROUND_GDK:
350 color.red = cellicon->foreground.red;
351 color.green = cellicon->foreground.green;
352 color.blue = cellicon->foreground.blue;
354 g_value_set_boxed(value, &color);
358 g_value_set_boolean(value, cellicon->focused);
360 case PROP_FIXED_WIDTH:
361 g_value_set_int(value, cellicon->fixed_width);
363 case PROP_FIXED_HEIGHT:
364 g_value_set_int(value, cellicon->fixed_height);
366 case PROP_BACKGROUND_SET:
367 g_value_set_boolean(value, cellicon->background_set);
369 case PROP_FOREGROUND_SET:
370 g_value_set_boolean(value, cellicon->foreground_set);
373 g_value_set_boolean(value, cellicon->show_text);
375 case PROP_SHOW_MARKS:
376 g_value_set_boolean(value, cellicon->show_marks);
379 g_value_set_int(value, cellicon->num_marks);
382 g_value_set_uint(value, cellicon->marks);
385 g_value_set_uint(value, cellicon->toggled_mark);
388 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
394 set_bg_color(GQvCellRendererIcon *cellicon,
399 if (!cellicon->background_set)
401 cellicon->background_set = TRUE;
402 g_object_notify(G_OBJECT(cellicon), "background_set");
405 cellicon->background.red = color->red;
406 cellicon->background.green = color->green;
407 cellicon->background.blue = color->blue;
411 if (cellicon->background_set)
413 cellicon->background_set = FALSE;
414 g_object_notify(G_OBJECT(cellicon), "background_set");
419 static void set_fg_color(GQvCellRendererIcon *cellicon,
424 if (!cellicon->foreground_set)
426 cellicon->foreground_set = TRUE;
427 g_object_notify(G_OBJECT(cellicon), "foreground_set");
430 cellicon->foreground.red = color->red;
431 cellicon->foreground.green = color->green;
432 cellicon->foreground.blue = color->blue;
436 if (cellicon->foreground_set)
438 cellicon->foreground_set = FALSE;
439 g_object_notify(G_OBJECT(cellicon), "foreground_set");
445 gqv_cell_renderer_icon_set_property(GObject *object,
450 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
458 pixbuf = (GdkPixbuf *) g_value_get_object(value);
459 if (pixbuf) g_object_ref(pixbuf);
460 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
461 cellicon->pixbuf = pixbuf;
468 text = cellicon->text;
469 cellicon->text = g_strdup(g_value_get_string(value));
472 g_object_notify(object, "text");
475 case PROP_BACKGROUND_GDK:
476 set_bg_color(cellicon, g_value_get_boxed(value));
478 case PROP_FOREGROUND_GDK:
479 set_fg_color(cellicon, g_value_get_boxed(value));
482 cellicon->focused = g_value_get_boolean(value);
484 case PROP_FIXED_WIDTH:
485 cellicon->fixed_width = g_value_get_int(value);
487 case PROP_FIXED_HEIGHT:
488 cellicon->fixed_height = g_value_get_int(value);
490 case PROP_BACKGROUND_SET:
491 cellicon->background_set = g_value_get_boolean(value);
493 case PROP_FOREGROUND_SET:
494 cellicon->foreground_set = g_value_get_boolean(value);
497 cellicon->show_text = g_value_get_boolean(value);
499 case PROP_SHOW_MARKS:
500 cellicon->show_marks = g_value_get_boolean(value);
503 cellicon->num_marks = g_value_get_int(value);
506 cellicon->marks = g_value_get_uint(value);
509 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
515 gqv_cell_renderer_icon_get_layout(GQvCellRendererIcon *cellicon, GtkWidget *widget, gboolean will_render)
520 width = (cellicon->fixed_width > 0) ? cellicon->fixed_width * PANGO_SCALE : -1;
522 layout = gtk_widget_create_pango_layout(widget, cellicon->text);
523 pango_layout_set_width(layout, width);
524 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
525 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
529 PangoAttrList *attr_list;
531 attr_list = pango_attr_list_new();
533 if (cellicon->foreground_set)
536 PangoAttribute *attr;
538 color = cellicon->foreground;
540 attr = pango_attr_foreground_new(color.red, color.green, color.blue);
542 attr->start_index = 0;
543 attr->end_index = G_MAXINT;
544 pango_attr_list_insert(attr_list, attr);
547 pango_layout_set_attributes(layout, attr_list);
548 pango_attr_list_unref(attr_list);
555 * gqv_cell_renderer_icon_new:
557 * Creates a new #GQvCellRendererIcon. Adjust rendering
558 * parameters using object properties. Object properties can be set
559 * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
560 * can bind a property to a value in a #GtkTreeModel. For example, you
561 * can bind the "pixbuf" property on the cell renderer to a pixbuf value
562 * in the model, thus rendering a different image in each row of the
565 * Return value: the new cell renderer
568 gqv_cell_renderer_icon_new(void)
570 return g_object_new(GQV_TYPE_CELL_RENDERER_ICON, NULL);
573 #if GTK_CHECK_VERSION(3,0,0)
574 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
576 const GdkRectangle *cell_area,
582 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
584 GdkRectangle *cell_area,
591 GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
595 gfloat xalign, yalign;
597 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
598 gtk_cell_renderer_get_alignment(cell, &xalign, &yalign);
600 if (cellicon->fixed_width > 0)
602 calc_width = cellicon->fixed_width;
606 calc_width = (cellicon->pixbuf) ? gdk_pixbuf_get_width(cellicon->pixbuf) : 0;
609 if (cellicon->fixed_height > 0)
611 calc_height = cellicon->fixed_height;
615 calc_height = (cellicon->pixbuf) ? gdk_pixbuf_get_height(cellicon->pixbuf) : 0;
618 if (cellicon->show_text && cellicon->text)
623 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, FALSE);
624 pango_layout_get_pixel_extents(layout, NULL, &rect);
625 g_object_unref(layout);
627 calc_width = MAX(calc_width, rect.width);
628 calc_height += rect.height;
631 if (cellicon->show_marks)
633 calc_height += TOGGLE_SPACING;
634 calc_width = MAX(calc_width, TOGGLE_SPACING * cellicon->num_marks);
637 calc_width += xpad * 2;
638 calc_height += ypad * 2;
640 if (x_offset) *x_offset = 0;
641 if (y_offset) *y_offset = 0;
643 if (cell_area && calc_width > 0 && calc_height > 0)
647 *x_offset = (xalign * (cell_area->width - calc_width - 2 * xpad));
648 *x_offset = MAX(*x_offset, 0) + xpad;
652 *y_offset = (yalign * (cell_area->height - calc_height - 2 * ypad));
653 *y_offset = MAX(*y_offset, 0) + ypad;
657 if (width) *width = calc_width;
658 if (height) *height = calc_height;
661 #if GTK_CHECK_VERSION(3,0,0)
662 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
665 const GdkRectangle *background_area,
666 const GdkRectangle *cell_area,
667 GtkCellRendererState flags)
670 GtkStyleContext *context = gtk_widget_get_style_context(widget);
673 gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
676 GdkRectangle *background_area,
677 GdkRectangle *cell_area,
678 GdkRectangle *expose_area,
679 GtkCellRendererState flags)
682 cairo_t *cr = gdk_cairo_create(window);
684 GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
687 GdkRectangle cell_rect;
692 pixbuf = cellicon->pixbuf;
693 text = cellicon->text;
695 if (!pixbuf && !text) return;
697 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
699 gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
700 &cell_rect.x, &cell_rect.y,
701 &cell_rect.width, &cell_rect.height);
705 cell_rect.width -= xpad * 2;
706 cell_rect.height -= ypad * 2;
708 if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
710 if (gtk_widget_has_focus(widget))
711 state = GTK_STATE_SELECTED;
713 state = GTK_STATE_ACTIVE;
717 if (gtk_widget_get_state(widget) == GTK_STATE_INSENSITIVE)
718 state = GTK_STATE_INSENSITIVE;
720 state = GTK_STATE_NORMAL;
723 #if GTK_CHECK_VERSION(3,0,0)
724 gtk_style_context_set_state(context, state);
729 GdkRectangle pix_rect;
730 GdkRectangle draw_rect;
732 pix_rect.width = gdk_pixbuf_get_width(pixbuf);
733 pix_rect.height = gdk_pixbuf_get_height(pixbuf);
735 pix_rect.x = cell_area->x + (cell_area->width - pix_rect.width) / 2;
737 if (cellicon->fixed_height > 0)
739 pix_rect.y = cell_area->y + ypad + (cellicon->fixed_height - pix_rect.height) / 2;
743 pix_rect.y = cell_area->y + cell_rect.y;
746 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect)
747 #if !GTK_CHECK_VERSION(3,0,0)
748 && gdk_rectangle_intersect(expose_area, &draw_rect, &draw_rect)
752 gdk_cairo_set_source_pixbuf(cr, pixbuf, pix_rect.x, pix_rect.y);
763 if (cellicon->show_text && text)
766 PangoRectangle text_rect;
767 GdkRectangle pix_rect;
768 GdkRectangle draw_rect;
769 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, TRUE);
770 pango_layout_get_pixel_extents(layout, NULL, &text_rect);
772 pix_rect.width = text_rect.width;
773 pix_rect.height = text_rect.height;
774 pix_rect.x = cell_area->x + xpad + (cell_rect.width - text_rect.width + 1) / 2;
775 pix_rect.y = cell_area->y + ypad + (cell_rect.height - text_rect.height);
777 if (cellicon->show_marks)
779 pix_rect.y -= TOGGLE_SPACING;
782 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect)
783 #if !GTK_CHECK_VERSION(3,0,0)
784 && gdk_rectangle_intersect(expose_area, &draw_rect, &draw_rect)
788 #if GTK_CHECK_VERSION(3,0,0)
789 gtk_render_layout(context, cr, pix_rect.x - text_rect.x, pix_rect.y, layout);
792 gtk_paint_layout(gtk_widget_get_style(widget), window,
796 pix_rect.x - text_rect.x, pix_rect.y,
800 g_object_unref(layout);
803 if (cellicon->show_marks)
805 GdkRectangle pix_rect;
806 GdkRectangle draw_rect;
809 pix_rect.width = TOGGLE_SPACING * cellicon->num_marks;
810 pix_rect.height = TOGGLE_SPACING;
811 pix_rect.x = cell_area->x + xpad + (cell_rect.width - pix_rect.width + 1) / 2 + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
812 pix_rect.y = cell_area->y + ypad + (cell_rect.height - pix_rect.height) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
814 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect)
815 #if !GTK_CHECK_VERSION(3,0,0)
816 && gdk_rectangle_intersect(expose_area, &draw_rect, &draw_rect)
820 for (i = 0; i < cellicon->num_marks; i++)
822 #if GTK_CHECK_VERSION(3,0,0)
823 state &= ~(GTK_STATE_FLAG_ACTIVE);
825 if ((cellicon->marks & (1 << i)))
826 state |= GTK_STATE_FLAG_ACTIVE;
831 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
833 TOGGLE_WIDTH, TOGGLE_WIDTH);
836 gtk_style_context_save(context);
837 gtk_style_context_set_state(context, state);
839 gtk_style_context_add_class(context, GTK_STYLE_CLASS_CHECK);
841 gtk_render_check(context, cr,
842 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
844 TOGGLE_WIDTH, TOGGLE_WIDTH);
845 gtk_style_context_restore(context);
849 gtk_paint_check(gtk_widget_get_style(widget), window,
850 state, (cellicon->marks & (1 << i)) ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
851 cell_area, widget, "cellcheck",
852 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
854 TOGGLE_WIDTH, TOGGLE_WIDTH);
860 if (cellicon->focused && gtk_widget_has_focus(widget))
862 #if GTK_CHECK_VERSION(3,0,0)
864 gtk_paint_focus(gtk_widget_get_style(widget), window,
868 cell_area->x, cell_area->y,
869 cell_area->width, cell_area->height);
872 #if !GTK_CHECK_VERSION(3,0,0)
877 #if GTK_CHECK_VERSION(3,0,0)
878 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
882 const GdkRectangle *background_area,
883 const GdkRectangle *cell_area,
884 GtkCellRendererState flags)
886 static gboolean gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
890 GdkRectangle *background_area,
891 GdkRectangle *cell_area,
892 GtkCellRendererState flags)
895 GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
896 GdkEventButton *bevent = &event->button;
898 if (cellicon->show_marks &&
899 event->type == GDK_BUTTON_PRESS &&
900 !(bevent->state & GDK_SHIFT_MASK ) &&
901 !(bevent->state & GDK_CONTROL_MASK ))
904 GdkRectangle cell_rect;
908 gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
910 gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
911 &cell_rect.x, &cell_rect.y,
912 &cell_rect.width, &cell_rect.height);
916 cell_rect.width -= xpad * 2;
917 cell_rect.height -= ypad * 2;
919 rect.width = TOGGLE_WIDTH;
920 rect.height = TOGGLE_WIDTH;
921 rect.y = cell_area->y + ypad + (cell_rect.height - TOGGLE_SPACING) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
922 for (i = 0; i < cellicon->num_marks; i++)
924 rect.x = cell_area->x + xpad + (cell_rect.width - TOGGLE_SPACING * cellicon->num_marks + 1) / 2 + i * TOGGLE_SPACING;
926 if (bevent->x >= rect.x && bevent->x < rect.x + rect.width &&
927 bevent->y >= rect.y && bevent->y < rect.y + rect.height)
929 cellicon->toggled_mark = i;
930 g_signal_emit(cell, toggle_cell_signals[TOGGLED], 0, path);
937 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */