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 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
47 GdkRectangle *rectangle,
52 static void gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
55 GdkRectangle *background_area,
56 GdkRectangle *cell_area,
57 GdkRectangle *expose_area,
58 GtkCellRendererState flags);
60 static gint gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
64 GdkRectangle *background_area,
65 GdkRectangle *cell_area,
66 GtkCellRendererState flags);
92 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
94 static gpointer parent_class;
97 gqv_cell_renderer_icon_get_type(void)
99 static GType cell_icon_type = 0;
103 static const GTypeInfo cell_icon_info =
105 sizeof(GQvCellRendererIconClass), /* class_size */
106 NULL, /* base_init */
107 NULL, /* base_finalize */
108 (GClassInitFunc) gqv_cell_renderer_icon_class_init, /* class_init */
109 NULL, /* class_finalize */
110 NULL, /* class_data */
111 sizeof(GQvCellRendererIcon), /* instance_size */
113 (GInstanceInitFunc) gqv_cell_renderer_icon_init, /* instance_init */
114 NULL, /* value_table */
117 cell_icon_type = g_type_register_static(GTK_TYPE_CELL_RENDERER,
118 "GQvCellRendererIcon",
122 return cell_icon_type;
126 gqv_cell_renderer_icon_init(GQvCellRendererIcon *cellicon)
128 GTK_CELL_RENDERER(cellicon)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
129 GTK_CELL_RENDERER(cellicon)->xpad = 2;
130 GTK_CELL_RENDERER(cellicon)->ypad = 2;
134 gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *class)
136 GObjectClass *object_class = G_OBJECT_CLASS(class);
137 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(class);
139 parent_class = g_type_class_peek_parent(class);
141 object_class->finalize = gqv_cell_renderer_icon_finalize;
143 object_class->get_property = gqv_cell_renderer_icon_get_property;
144 object_class->set_property = gqv_cell_renderer_icon_set_property;
146 cell_class->get_size = gqv_cell_renderer_icon_get_size;
147 cell_class->render = gqv_cell_renderer_icon_render;
148 cell_class->activate = gqv_cell_renderer_icon_activate;
150 g_object_class_install_property(object_class,
152 g_param_spec_object("pixbuf",
154 "The pixbuf to render",
158 g_object_class_install_property(object_class,
160 g_param_spec_string("text",
166 g_object_class_install_property(object_class,
168 g_param_spec_boxed("background_gdk",
170 "Background color as a GdkColor",
174 g_object_class_install_property(object_class,
176 g_param_spec_boxed("foreground_gdk",
178 "Foreground color as a GdkColor",
182 g_object_class_install_property(object_class,
184 g_param_spec_boolean("has_focus",
186 "Draw focus indicator",
190 g_object_class_install_property(object_class,
192 g_param_spec_int("fixed_width",
195 -1, FIXED_ICON_SIZE_MAX,
199 g_object_class_install_property(object_class,
201 g_param_spec_int("fixed_height",
203 "Height of icon excluding text",
204 -1, FIXED_ICON_SIZE_MAX,
208 g_object_class_install_property(object_class,
210 g_param_spec_boolean("background_set",
212 "Whether this tag affects the background color",
216 g_object_class_install_property(object_class,
218 g_param_spec_boolean("foreground_set",
220 "Whether this tag affects the foreground color",
224 g_object_class_install_property(object_class,
226 g_param_spec_boolean("show_text",
228 "Whether the text is displayed",
232 g_object_class_install_property(object_class,
234 g_param_spec_boolean("show_marks",
236 "Whether the marks are displayed",
240 g_object_class_install_property(object_class,
242 g_param_spec_int("num_marks",
249 g_object_class_install_property(object_class,
251 g_param_spec_uint("marks",
258 g_object_class_install_property(object_class,
260 g_param_spec_uint("toggled_mark",
266 toggle_cell_signals[TOGGLED] =
267 g_signal_new("toggled",
268 G_OBJECT_CLASS_TYPE (object_class),
270 G_STRUCT_OFFSET (GQvCellRendererIconClass, toggled),
272 g_cclosure_marshal_VOID__STRING,
279 gqv_cell_renderer_icon_finalize(GObject *object)
281 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
283 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
285 g_free(cellicon->text);
287 (*(G_OBJECT_CLASS(parent_class))->finalize)(object);
291 gqv_cell_renderer_icon_get_property(GObject *object,
296 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
301 g_value_set_object(value, cellicon->pixbuf ? G_OBJECT(cellicon->pixbuf) : NULL);
304 g_value_set_string(value, cellicon->text);
306 case PROP_BACKGROUND_GDK:
310 color.red = cellicon->background.red;
311 color.green = cellicon->background.green;
312 color.blue = cellicon->background.blue;
314 g_value_set_boxed(value, &color);
317 case PROP_FOREGROUND_GDK:
321 color.red = cellicon->foreground.red;
322 color.green = cellicon->foreground.green;
323 color.blue = cellicon->foreground.blue;
325 g_value_set_boxed(value, &color);
329 g_value_set_boolean(value, cellicon->focused);
331 case PROP_FIXED_WIDTH:
332 g_value_set_int(value, cellicon->fixed_width);
334 case PROP_FIXED_HEIGHT:
335 g_value_set_int(value, cellicon->fixed_height);
337 case PROP_BACKGROUND_SET:
338 g_value_set_boolean(value, cellicon->background_set);
340 case PROP_FOREGROUND_SET:
341 g_value_set_boolean(value, cellicon->foreground_set);
344 g_value_set_boolean(value, cellicon->show_text);
346 case PROP_SHOW_MARKS:
347 g_value_set_boolean(value, cellicon->show_marks);
350 g_value_set_int(value, cellicon->num_marks);
353 g_value_set_uint(value, cellicon->marks);
356 g_value_set_uint(value, cellicon->toggled_mark);
359 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
365 set_bg_color(GQvCellRendererIcon *cellicon,
370 if (!cellicon->background_set)
372 cellicon->background_set = TRUE;
373 g_object_notify(G_OBJECT(cellicon), "background_set");
376 cellicon->background.red = color->red;
377 cellicon->background.green = color->green;
378 cellicon->background.blue = color->blue;
382 if (cellicon->background_set)
384 cellicon->background_set = FALSE;
385 g_object_notify(G_OBJECT(cellicon), "background_set");
390 static void set_fg_color(GQvCellRendererIcon *cellicon,
395 if (!cellicon->foreground_set)
397 cellicon->foreground_set = TRUE;
398 g_object_notify(G_OBJECT(cellicon), "foreground_set");
401 cellicon->foreground.red = color->red;
402 cellicon->foreground.green = color->green;
403 cellicon->foreground.blue = color->blue;
407 if (cellicon->foreground_set)
409 cellicon->foreground_set = FALSE;
410 g_object_notify(G_OBJECT(cellicon), "foreground_set");
416 gqv_cell_renderer_icon_set_property(GObject *object,
421 GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
429 pixbuf = (GdkPixbuf *) g_value_get_object(value);
430 if (pixbuf) g_object_ref(pixbuf);
431 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
432 cellicon->pixbuf = pixbuf;
439 text = cellicon->text;
440 cellicon->text = g_strdup(g_value_get_string(value));
443 g_object_notify(object, "text");
446 case PROP_BACKGROUND_GDK:
447 set_bg_color(cellicon, g_value_get_boxed(value));
449 case PROP_FOREGROUND_GDK:
450 set_fg_color(cellicon, g_value_get_boxed(value));
453 cellicon->focused = g_value_get_boolean(value);
455 case PROP_FIXED_WIDTH:
456 cellicon->fixed_width = g_value_get_int(value);
458 case PROP_FIXED_HEIGHT:
459 cellicon->fixed_height = g_value_get_int(value);
461 case PROP_BACKGROUND_SET:
462 cellicon->background_set = g_value_get_boolean(value);
464 case PROP_FOREGROUND_SET:
465 cellicon->foreground_set = g_value_get_boolean(value);
468 cellicon->show_text = g_value_get_boolean(value);
470 case PROP_SHOW_MARKS:
471 cellicon->show_marks = g_value_get_boolean(value);
474 cellicon->num_marks = g_value_get_int(value);
477 cellicon->marks = g_value_get_uint(value);
480 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
486 gqv_cell_renderer_icon_get_layout(GQvCellRendererIcon *cellicon, GtkWidget *widget, gboolean will_render)
491 width = (cellicon->fixed_width > 0) ? cellicon->fixed_width * PANGO_SCALE : -1;
493 layout = gtk_widget_create_pango_layout(widget, cellicon->text);
494 pango_layout_set_width(layout, width);
495 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
496 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
500 PangoAttrList *attr_list;
502 attr_list = pango_attr_list_new();
504 if (cellicon->foreground_set)
507 PangoAttribute *attr;
509 color = cellicon->foreground;
511 attr = pango_attr_foreground_new(color.red, color.green, color.blue);
513 attr->start_index = 0;
514 attr->end_index = G_MAXINT;
515 pango_attr_list_insert(attr_list, attr);
518 pango_layout_set_attributes(layout, attr_list);
519 pango_attr_list_unref(attr_list);
526 * gqv_cell_renderer_icon_new:
528 * Creates a new #GQvCellRendererIcon. Adjust rendering
529 * parameters using object properties. Object properties can be set
530 * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
531 * can bind a property to a value in a #GtkTreeModel. For example, you
532 * can bind the "pixbuf" property on the cell renderer to a pixbuf value
533 * in the model, thus rendering a different image in each row of the
536 * Return value: the new cell renderer
539 gqv_cell_renderer_icon_new(void)
541 return g_object_new(GQV_TYPE_CELL_RENDERER_ICON, NULL);
545 gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
547 GdkRectangle *cell_area,
553 GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
557 if (cellicon->fixed_width > 0)
559 calc_width = cellicon->fixed_width;
563 calc_width = (cellicon->pixbuf) ? gdk_pixbuf_get_width(cellicon->pixbuf) : 0;
566 if (cellicon->fixed_height > 0)
568 calc_height = cellicon->fixed_height;
572 calc_height = (cellicon->pixbuf) ? gdk_pixbuf_get_height(cellicon->pixbuf) : 0;
575 if (cellicon->show_text && cellicon->text)
580 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, FALSE);
581 pango_layout_get_pixel_extents(layout, NULL, &rect);
582 g_object_unref(layout);
584 calc_width = MAX(calc_width, rect.width);
585 calc_height += rect.height;
588 if (cellicon->show_marks)
590 calc_height += TOGGLE_SPACING;
591 calc_width = MAX(calc_width, TOGGLE_SPACING * cellicon->num_marks);
594 calc_width += (gint)cell->xpad * 2;
595 calc_height += (gint)cell->ypad * 2;
597 if (x_offset) *x_offset = 0;
598 if (y_offset) *y_offset = 0;
600 if (cell_area && calc_width > 0 && calc_height > 0)
604 *x_offset = (cell->xalign * (cell_area->width - calc_width - 2 * cell->xpad));
605 *x_offset = MAX(*x_offset, 0) + cell->xpad;
609 *y_offset = (cell->yalign * (cell_area->height - calc_height - 2 * cell->ypad));
610 *y_offset = MAX(*y_offset, 0) + cell->ypad;
614 if (width) *width = calc_width;
615 if (height) *height = calc_height;
619 gqv_cell_renderer_icon_render(GtkCellRenderer *cell,
622 GdkRectangle *background_area,
623 GdkRectangle *cell_area,
624 GdkRectangle *expose_area,
625 GtkCellRendererState flags)
628 GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
631 GdkRectangle cell_rect;
634 pixbuf = cellicon->pixbuf;
635 text = cellicon->text;
637 if (!pixbuf && !text) return;
639 gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
640 &cell_rect.x, &cell_rect.y,
641 &cell_rect.width, &cell_rect.height);
643 cell_rect.x += cell->xpad;
644 cell_rect.y += cell->ypad;
645 cell_rect.width -= cell->xpad * 2;
646 cell_rect.height -= cell->ypad * 2;
648 if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
650 #if GTK_CHECK_VERSION(2,20,0)
651 if (gtk_widget_has_focus(widget))
653 if (GTK_WIDGET_HAS_FOCUS(widget))
655 state = GTK_STATE_SELECTED;
657 state = GTK_STATE_ACTIVE;
661 #if GTK_CHECK_VERSION(2,20,0)
662 if (gtk_widget_get_state(widget) == GTK_STATE_INSENSITIVE)
664 if (GTK_WIDGET_STATE(widget) == GTK_STATE_INSENSITIVE)
666 state = GTK_STATE_INSENSITIVE;
668 state = GTK_STATE_NORMAL;
673 GdkRectangle pix_rect;
674 GdkRectangle draw_rect;
676 pix_rect.width = gdk_pixbuf_get_width(pixbuf);
677 pix_rect.height = gdk_pixbuf_get_height(pixbuf);
679 pix_rect.x = cell_area->x + (cell_area->width - pix_rect.width) / 2;
681 if (cellicon->fixed_height > 0)
683 pix_rect.y = cell_area->y + cell->ypad + (cellicon->fixed_height - pix_rect.height) / 2;
687 pix_rect.y = cell_area->y + cell_rect.y;
690 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect) &&
691 gdk_rectangle_intersect(expose_area, &draw_rect, &draw_rect))
693 gdk_draw_pixbuf(window,
694 widget->style->black_gc,
696 /* pixbuf 0, 0 is at pix_rect.x, pix_rect.y */
697 draw_rect.x - pix_rect.x,
698 draw_rect.y - pix_rect.y,
703 GDK_RGB_DITHER_NORMAL,
708 if (cellicon->show_text && text)
711 PangoRectangle text_rect;
712 GdkRectangle pix_rect;
713 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 + cell->xpad + (cell_rect.width - text_rect.width + 1) / 2;
721 pix_rect.y = cell_area->y + cell->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) &&
729 gdk_rectangle_intersect(expose_area, &draw_rect, &draw_rect))
731 gtk_paint_layout(widget->style, window,
735 pix_rect.x - text_rect.x, pix_rect.y,
739 g_object_unref(layout);
742 if (cellicon->show_marks)
744 GdkRectangle pix_rect;
745 GdkRectangle draw_rect;
748 pix_rect.width = TOGGLE_SPACING * cellicon->num_marks;
749 pix_rect.height = TOGGLE_SPACING;
750 pix_rect.x = cell_area->x + cell->xpad + (cell_rect.width - pix_rect.width + 1) / 2 + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
751 pix_rect.y = cell_area->y + cell->ypad + (cell_rect.height - pix_rect.height) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
753 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect) &&
754 gdk_rectangle_intersect(expose_area, &draw_rect, &draw_rect))
756 for (i = 0; i < cellicon->num_marks; i++)
758 gtk_paint_check(widget->style, window,
759 state, (cellicon->marks & (1 << i)) ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
760 cell_area, widget, "cellcheck",
761 pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
763 TOGGLE_WIDTH, TOGGLE_WIDTH);
768 #if GTK_CHECK_VERSION(2,20,0)
769 if (cellicon->focused && gtk_widget_has_focus(widget))
771 if (cellicon->focused && GTK_WIDGET_HAS_FOCUS(widget))
774 gtk_paint_focus(widget->style, window,
778 cell_area->x, cell_area->y,
779 cell_area->width, cell_area->height);
784 gqv_cell_renderer_icon_activate(GtkCellRenderer *cell,
788 GdkRectangle *background_area,
789 GdkRectangle *cell_area,
790 GtkCellRendererState flags)
792 GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
793 GdkEventButton *bevent = &event->button;
795 if (cellicon->show_marks &&
796 event->type == GDK_BUTTON_PRESS &&
797 !(bevent->state & GDK_SHIFT_MASK ) &&
798 !(bevent->state & GDK_CONTROL_MASK ))
801 GdkRectangle cell_rect;
804 gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
805 &cell_rect.x, &cell_rect.y,
806 &cell_rect.width, &cell_rect.height);
808 cell_rect.x += cell->xpad;
809 cell_rect.y += cell->ypad;
810 cell_rect.width -= cell->xpad * 2;
811 cell_rect.height -= cell->ypad * 2;
813 rect.width = TOGGLE_WIDTH;
814 rect.height = TOGGLE_WIDTH;
815 rect.y = cell_area->y + cell->ypad + (cell_rect.height - TOGGLE_SPACING) + (TOGGLE_SPACING - TOGGLE_WIDTH) / 2;
816 for (i = 0; i < cellicon->num_marks; i++)
818 rect.x = cell_area->x + cell->xpad + (cell_rect.width - TOGGLE_SPACING * cellicon->num_marks + 1) / 2 + i * TOGGLE_SPACING;
820 if (bevent->x >= rect.x && bevent->x < rect.x + rect.width &&
821 bevent->y >= rect.y && bevent->y < rect.y + rect.height)
823 cellicon->toggled_mark = i;
824 g_signal_emit(cell, toggle_cell_signals[TOGGLED], 0, path);
831 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */