Indentation fixes.
[geeqie.git] / src / cellrenderericon.c
1 /* cellrenderericon.c, based on:
2  *
3  * gtkcellrendererpixbuf.c
4  * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 #include <stdlib.h>
23 #include "cellrenderericon.h"
24 #include "intl.h"
25
26
27 #define FIXED_ICON_SIZE_MAX 512
28
29 static void gqv_cell_renderer_icon_get_property(GObject         *object,
30                                                 guint           param_id,
31                                                 GValue          *value,
32                                                 GParamSpec      *pspec);
33 static void gqv_cell_renderer_icon_set_property(GObject         *object,
34                                                 guint           param_id,
35                                                 const GValue    *value,
36                                                 GParamSpec      *pspec);
37 static void gqv_cell_renderer_icon_init(GQvCellRendererIcon *celltext);
38 static void gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *class);
39 static void gqv_cell_renderer_icon_finalize(GObject *object);
40 static void gqv_cell_renderer_icon_get_size(GtkCellRenderer     *cell,
41                                             GtkWidget           *widget,
42                                             GdkRectangle        *rectangle,
43                                             gint                *x_offset,
44                                             gint                *y_offset,
45                                             gint                *width,
46                                             gint                *height);
47 static void gqv_cell_renderer_icon_render(GtkCellRenderer       *cell,
48                                            GdkWindow            *window,
49                                            GtkWidget            *widget,
50                                            GdkRectangle         *background_area,
51                                            GdkRectangle         *cell_area,
52                                            GdkRectangle         *expose_area,
53                                            GtkCellRendererState flags);
54
55
56 enum {
57         PROP_ZERO,
58         PROP_PIXBUF,
59         PROP_TEXT,
60         PROP_BACKGROUND_GDK,
61         PROP_FOREGROUND_GDK,
62         PROP_FOCUSED,
63         PROP_FIXED_WIDTH,
64         PROP_FIXED_HEIGHT,
65
66         PROP_BACKGROUND_SET,
67         PROP_FOREGROUND_SET,
68         PROP_SHOW_TEXT
69 };
70
71 static gpointer parent_class;
72
73 GType
74 gqv_cell_renderer_icon_get_type(void)
75 {
76         static GType cell_icon_type = 0;
77
78         if (!cell_icon_type)
79                 {
80                 static const GTypeInfo cell_icon_info =
81                         {
82                         sizeof(GQvCellRendererIconClass),
83                         NULL,           /* base_init */
84                         NULL,           /* base_finalize */
85                         (GClassInitFunc) gqv_cell_renderer_icon_class_init,
86                         NULL,           /* class_finalize */
87                         NULL,           /* class_data */
88                         sizeof(GQvCellRendererIcon),
89                         0,              /* n_preallocs */
90                         (GInstanceInitFunc) gqv_cell_renderer_icon_init,
91                         };
92
93                 cell_icon_type = g_type_register_static(GTK_TYPE_CELL_RENDERER,
94                                                         "GQvCellRendererIcon",
95                                                         &cell_icon_info, 0);
96                 }
97
98         return cell_icon_type;
99 }
100
101 static void
102 gqv_cell_renderer_icon_init(GQvCellRendererIcon *cellicon)
103 {
104         GTK_CELL_RENDERER(cellicon)->xpad = 2;
105         GTK_CELL_RENDERER(cellicon)->ypad = 2;
106 }
107
108 static void
109 gqv_cell_renderer_icon_class_init(GQvCellRendererIconClass *class)
110 {
111         GObjectClass *object_class = G_OBJECT_CLASS(class);
112         GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(class);
113
114         parent_class = g_type_class_peek_parent(class);
115
116         object_class->finalize = gqv_cell_renderer_icon_finalize;
117
118         object_class->get_property = gqv_cell_renderer_icon_get_property;
119         object_class->set_property = gqv_cell_renderer_icon_set_property;
120
121         cell_class->get_size = gqv_cell_renderer_icon_get_size;
122         cell_class->render = gqv_cell_renderer_icon_render;
123
124         g_object_class_install_property(object_class,
125                                         PROP_PIXBUF,
126                                         g_param_spec_object("pixbuf",
127                                                         _("Pixbuf Object"),
128                                                         _("The pixbuf to render"),
129                                                         GDK_TYPE_PIXBUF,
130                                                         G_PARAM_READWRITE));
131
132         g_object_class_install_property(object_class,
133                                         PROP_TEXT,
134                                         g_param_spec_string("text",
135                                                         _("Text"),
136                                                         _("Text to render"),
137                                                         NULL,
138                                                         G_PARAM_READWRITE));
139
140         g_object_class_install_property(object_class,
141                                         PROP_BACKGROUND_GDK,
142                                         g_param_spec_boxed("background_gdk",
143                                                         _("Background color"),
144                                                         _("Background color as a GdkColor"),
145                                                         GDK_TYPE_COLOR,
146                                                         G_PARAM_READWRITE));
147
148         g_object_class_install_property(object_class,
149                                         PROP_FOREGROUND_GDK,
150                                         g_param_spec_boxed("foreground_gdk",
151                                                         _("Foreground color"),
152                                                         _("Foreground color as a GdkColor"),
153                                                         GDK_TYPE_COLOR,
154                                                         G_PARAM_READWRITE));
155
156         g_object_class_install_property(object_class,
157                                         PROP_FOCUSED,
158                                         g_param_spec_boolean("has_focus",
159                                                         _("Focus"),
160                                                         _("Draw focus indicator"),
161                                                         FALSE,
162                                                         G_PARAM_READWRITE));
163
164         g_object_class_install_property(object_class,
165                                         PROP_FIXED_WIDTH,
166                                         g_param_spec_int("fixed_width",
167                                                         _("Fixed width"),
168                                                         _("Width of cell"),
169                                                         -1, FIXED_ICON_SIZE_MAX,
170                                                         -1,
171                                                         G_PARAM_READWRITE));
172
173         g_object_class_install_property(object_class,
174                                         PROP_FIXED_HEIGHT,
175                                         g_param_spec_int("fixed_height",
176                                                         _("Fixed height"),
177                                                         _("Height of icon excluding text"),
178                                                         -1, FIXED_ICON_SIZE_MAX,
179                                                         -1,
180                                                         G_PARAM_READWRITE));
181
182         g_object_class_install_property(object_class,
183                                         PROP_BACKGROUND_SET,
184                                         g_param_spec_boolean("background_set",
185                                                         _("Background set"),
186                                                         _("Whether this tag affects the background color"),
187                                                         FALSE,
188                                                         G_PARAM_READWRITE));
189
190         g_object_class_install_property(object_class,
191                                         PROP_FOREGROUND_SET,
192                                         g_param_spec_boolean("foreground_set",
193                                                         _("Foreground set"),
194                                                         _("Whether this tag affects the foreground color"),
195                                                         FALSE,
196                                                         G_PARAM_READWRITE));
197
198         g_object_class_install_property(object_class,
199                                         PROP_SHOW_TEXT,
200                                         g_param_spec_boolean("show_text",
201                                                         _("Show text"),
202                                                         _("Whether the text is displayed"),
203                                                         TRUE,
204                                                         G_PARAM_READWRITE));
205 }
206
207 static void
208 gqv_cell_renderer_icon_finalize(GObject *object)
209 {
210         GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
211
212         if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
213
214         g_free(cellicon->text);
215
216         (*(G_OBJECT_CLASS(parent_class))->finalize)(object);
217 }
218
219 static void
220 gqv_cell_renderer_icon_get_property(GObject     *object,
221                                     guint       param_id,
222                                     GValue      *value,
223                                     GParamSpec  *pspec)
224 {
225         GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
226
227         switch (param_id)
228         {
229         case PROP_PIXBUF:
230                 g_value_set_object(value, cellicon->pixbuf ? G_OBJECT(cellicon->pixbuf) : NULL);
231                 break;
232         case PROP_TEXT:
233                 g_value_set_string(value, cellicon->text);
234                 break;
235         case PROP_BACKGROUND_GDK:
236                 {
237                 GdkColor color;
238
239                 color.red = cellicon->background.red;
240                 color.green = cellicon->background.green;
241                 color.blue = cellicon->background.blue;
242
243                 g_value_set_boxed(value, &color);
244                 }
245                 break;
246         case PROP_FOREGROUND_GDK:
247                 {
248                 GdkColor color;
249
250                 color.red = cellicon->foreground.red;
251                 color.green = cellicon->foreground.green;
252                 color.blue = cellicon->foreground.blue;
253
254                 g_value_set_boxed(value, &color);
255                 }
256                 break;
257         case PROP_FOCUSED:
258                 g_value_set_boolean(value, cellicon->focused);
259                 break;
260         case PROP_FIXED_WIDTH:
261                 g_value_set_int(value, cellicon->fixed_width);
262                 break;
263         case PROP_FIXED_HEIGHT:
264                 g_value_set_int(value, cellicon->fixed_height);
265                 break;
266         case PROP_BACKGROUND_SET:
267                 g_value_set_boolean(value, cellicon->background_set);
268                 break;
269         case PROP_FOREGROUND_SET:
270                 g_value_set_boolean(value, cellicon->foreground_set);
271                 break;
272         case PROP_SHOW_TEXT:
273                 g_value_set_boolean(value, cellicon->show_text);
274                 break;
275         default:
276                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
277                 break;
278         }
279 }
280
281 static void
282 set_bg_color(GQvCellRendererIcon *cellicon,
283              GdkColor             *color)
284 {
285         if (color)
286                 {
287                 if (!cellicon->background_set)
288                         {
289                         cellicon->background_set = TRUE;
290                         g_object_notify(G_OBJECT(cellicon), "background_set");
291                         }
292
293                 cellicon->background.red = color->red;
294                 cellicon->background.green = color->green;
295                 cellicon->background.blue = color->blue;
296                 }
297         else
298                 {
299                 if (cellicon->background_set)
300                         {
301                         cellicon->background_set = FALSE;
302                         g_object_notify(G_OBJECT(cellicon), "background_set");
303                         }
304                 }
305 }
306
307 static void set_fg_color(GQvCellRendererIcon *cellicon,
308                          GdkColor             *color)
309 {
310         if (color)
311                 {
312                 if (!cellicon->foreground_set)
313                         {
314                         cellicon->foreground_set = TRUE;
315                         g_object_notify(G_OBJECT(cellicon), "foreground_set");
316                         }
317
318                 cellicon->foreground.red = color->red;
319                 cellicon->foreground.green = color->green;
320                 cellicon->foreground.blue = color->blue;
321                 }
322         else
323                 {
324                 if (cellicon->foreground_set)
325                         {
326                         cellicon->foreground_set = FALSE;
327                         g_object_notify(G_OBJECT(cellicon), "foreground_set");
328                         }
329                 }
330 }
331
332 static void
333 gqv_cell_renderer_icon_set_property(GObject             *object,
334                                     guint               param_id,
335                                     const GValue        *value,
336                                     GParamSpec          *pspec)
337 {
338         GQvCellRendererIcon *cellicon = GQV_CELL_RENDERER_ICON(object);
339
340         switch (param_id)
341         {
342         case PROP_PIXBUF:
343                 {
344                 GdkPixbuf *pixbuf;
345
346                 pixbuf = (GdkPixbuf*) g_value_get_object(value);
347                 if (pixbuf) g_object_ref(pixbuf);
348                 if (cellicon->pixbuf) g_object_unref(cellicon->pixbuf);
349                 cellicon->pixbuf = pixbuf;
350                 }
351                 break;
352         case PROP_TEXT:
353                 {
354                 gchar *text;
355
356                 text = cellicon->text;
357                 cellicon->text = g_strdup(g_value_get_string(value));
358                 g_free(text);
359
360                 g_object_notify(object, "text");
361                 }
362                 break;
363         case PROP_BACKGROUND_GDK:
364                 set_bg_color(cellicon, g_value_get_boxed(value));
365                 break;
366         case PROP_FOREGROUND_GDK:
367                 set_fg_color(cellicon, g_value_get_boxed(value));
368                 break;
369         case PROP_FOCUSED:
370                 cellicon->focused = g_value_get_boolean(value);
371                 break;
372         case PROP_FIXED_WIDTH:
373                 cellicon->fixed_width = g_value_get_int(value);
374                 break;
375         case PROP_FIXED_HEIGHT:
376                 cellicon->fixed_height = g_value_get_int(value);
377                 break;
378         case PROP_BACKGROUND_SET:
379                 cellicon->background_set = g_value_get_boolean(value);
380                 break;
381         case PROP_FOREGROUND_SET:
382                 cellicon->foreground_set = g_value_get_boolean(value);
383                 break;
384         case PROP_SHOW_TEXT:
385                 cellicon->show_text = g_value_get_boolean(value);
386                 break;
387         default:
388                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
389                 break;
390         }
391 }
392
393 static PangoLayout *
394 gqv_cell_renderer_icon_get_layout(GQvCellRendererIcon *cellicon, GtkWidget *widget, gboolean will_render)
395 {
396         PangoLayout *layout;
397         gint width;
398
399         width = (cellicon->fixed_width > 0) ? cellicon->fixed_width * PANGO_SCALE : -1;
400
401         layout = gtk_widget_create_pango_layout(widget, cellicon->text);
402         pango_layout_set_width(layout, width);
403         pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
404         pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
405
406         if (will_render)
407                 {
408                 PangoAttrList *attr_list;
409
410                 attr_list = pango_attr_list_new();
411
412                 if (cellicon->foreground_set)
413                         {
414                         PangoColor color;
415                         PangoAttribute *attr;
416
417                         color = cellicon->foreground;
418
419                         attr = pango_attr_foreground_new(color.red, color.green, color.blue);
420
421                         attr->start_index = 0;
422                         attr->end_index = G_MAXINT;
423                         pango_attr_list_insert(attr_list, attr);
424                         }
425
426                 pango_layout_set_attributes(layout, attr_list);
427                 pango_attr_list_unref(attr_list);
428                 }
429
430         return layout;
431 }
432
433 /**
434  * gqv_cell_renderer_icon_new:
435  *
436  * Creates a new #GQvCellRendererIcon. Adjust rendering
437  * parameters using object properties. Object properties can be set
438  * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
439  * can bind a property to a value in a #GtkTreeModel. For example, you
440  * can bind the "pixbuf" property on the cell renderer to a pixbuf value
441  * in the model, thus rendering a different image in each row of the
442  * #GtkTreeView.
443  *
444  * Return value: the new cell renderer
445  **/
446 GtkCellRenderer *
447 gqv_cell_renderer_icon_new(void)
448 {
449         return g_object_new(GQV_TYPE_CELL_RENDERER_ICON, NULL);
450 }
451
452 static void
453 gqv_cell_renderer_icon_get_size(GtkCellRenderer *cell,
454                                 GtkWidget       *widget,
455                                 GdkRectangle    *cell_area,
456                                 gint            *x_offset,
457                                 gint            *y_offset,
458                                 gint            *width,
459                                 gint            *height)
460 {
461         GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
462         gint calc_width;
463         gint calc_height;
464
465         if (cellicon->fixed_width > 0)
466                 {
467                 calc_width = cellicon->fixed_width;
468                 }
469         else
470                 {
471                 calc_width = (cellicon->pixbuf) ? gdk_pixbuf_get_width(cellicon->pixbuf) : 0;
472                 }
473
474         if (cellicon->fixed_height > 0)
475                 {
476                 calc_height = cellicon->fixed_height;
477                 }
478         else
479                 {
480                 calc_height = (cellicon->pixbuf) ? gdk_pixbuf_get_height(cellicon->pixbuf) : 0;
481                 }
482
483         if (cellicon->show_text && cellicon->text)
484                 {
485                 PangoLayout *layout;
486                 PangoRectangle rect;
487
488                 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, FALSE);
489                 pango_layout_get_pixel_extents(layout, NULL, &rect);
490                 g_object_unref(layout);
491
492                 calc_width = MAX(calc_width, rect.width);
493                 calc_height += rect.height;
494                 }
495
496         calc_width += (gint)cell->xpad * 2;
497         calc_height += (gint)cell->ypad * 2;
498
499         if (x_offset) *x_offset = 0;
500         if (y_offset) *y_offset = 0;
501
502         if (cell_area && calc_width > 0 && calc_height > 0)
503                 {
504                 if (x_offset)
505                         {
506                         *x_offset = (cell->xalign * (cell_area->width - calc_width - 2 * cell->xpad));
507                         *x_offset = MAX(*x_offset, 0) + cell->xpad;
508                         }
509                 if (y_offset)
510                         {
511                         *y_offset = (cell->yalign * (cell_area->height - calc_height - 2 * cell->ypad));
512                         *y_offset = MAX(*y_offset, 0) + cell->ypad;
513                         }
514                 }
515
516         if (width) *width = calc_width;
517         if (height) *height = calc_height;
518 }
519
520 static void
521 gqv_cell_renderer_icon_render(GtkCellRenderer           *cell,
522                               GdkWindow                 *window,
523                               GtkWidget                 *widget,
524                               GdkRectangle              *background_area,
525                               GdkRectangle              *cell_area,
526                               GdkRectangle              *expose_area,
527                               GtkCellRendererState      flags)
528
529 {
530         GQvCellRendererIcon *cellicon = (GQvCellRendererIcon *) cell;
531         GdkPixbuf *pixbuf;
532         const gchar *text;
533         GdkRectangle cell_rect;
534         GtkStateType state;
535
536         pixbuf = cellicon->pixbuf;
537         text = cellicon->text;
538
539         if (!pixbuf && !text) return;
540
541         gqv_cell_renderer_icon_get_size(cell, widget, cell_area,
542                                         &cell_rect.x, &cell_rect.y,
543                                         &cell_rect.width, &cell_rect.height);
544
545         cell_rect.x += cell->xpad;
546         cell_rect.y += cell->ypad;
547         cell_rect.width -= cell->xpad * 2;
548         cell_rect.height -= cell->ypad * 2;
549
550         if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
551                 {
552                 if (GTK_WIDGET_HAS_FOCUS(widget))
553                         state = GTK_STATE_SELECTED;
554                 else
555                         state = GTK_STATE_ACTIVE;
556                 }
557         else
558                 {
559                 if (GTK_WIDGET_STATE(widget) == GTK_STATE_INSENSITIVE)
560                         state = GTK_STATE_INSENSITIVE;
561                 else
562                         state = GTK_STATE_NORMAL;
563                 }
564
565         if (pixbuf)
566                 {
567                 GdkRectangle pix_rect;
568                 GdkRectangle draw_rect;
569
570                 pix_rect.width = gdk_pixbuf_get_width(pixbuf);
571                 pix_rect.height = gdk_pixbuf_get_height(pixbuf);
572
573                 pix_rect.x = cell_area->x + (cell_area->width - pix_rect.width) / 2;
574
575                 if (cellicon->fixed_height > 0)
576                         {
577                         pix_rect.y = cell_area->y + cell->ypad + (cellicon->fixed_height - pix_rect.height) / 2;
578                         }
579                 else
580                         {
581                         pix_rect.y = cell_area->y + cell_rect.y;
582                         }
583
584                 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect) &&
585                     gdk_rectangle_intersect(expose_area, &draw_rect, &draw_rect))
586                         {
587                         gdk_draw_pixbuf(window,
588                                         widget->style->black_gc,
589                                         pixbuf,
590                                         /* pixbuf 0, 0 is at pix_rect.x, pix_rect.y */
591                                         draw_rect.x - pix_rect.x,
592                                         draw_rect.y - pix_rect.y,
593                                         draw_rect.x,
594                                         draw_rect.y,
595                                         draw_rect.width,
596                                         draw_rect.height,
597                                         GDK_RGB_DITHER_NORMAL,
598                                         0, 0);
599                         }
600                 }
601
602         if (cellicon->show_text && text)
603                 {
604                 PangoLayout *layout;
605                 PangoRectangle text_rect;
606                 GdkRectangle pix_rect;
607                 GdkRectangle draw_rect;
608
609                 layout = gqv_cell_renderer_icon_get_layout(cellicon, widget, TRUE);
610                 pango_layout_get_pixel_extents(layout, NULL, &text_rect);
611
612                 pix_rect.width = text_rect.width;
613                 pix_rect.height = text_rect.height;
614                 pix_rect.x = cell_area->x + cell->xpad + (cell_rect.width - text_rect.width + 1) / 2;
615                 pix_rect.y = cell_area->y + cell->ypad + (cell_rect.height - text_rect.height);
616
617                 if (gdk_rectangle_intersect(cell_area, &pix_rect, &draw_rect) &&
618                     gdk_rectangle_intersect(expose_area, &draw_rect, &draw_rect))
619                         {
620                         gtk_paint_layout(widget->style, window,
621                                          state, TRUE,
622                                          cell_area, widget,
623                                          "cellrenderertext",
624                                          pix_rect.x - text_rect.x, pix_rect.y,
625                                          layout);
626                         }
627
628                 g_object_unref(layout);
629                 }
630
631         if (cellicon->focused && GTK_WIDGET_HAS_FOCUS(widget))
632                 {
633                 gtk_paint_focus(widget->style, window,
634                                 state,
635                                 cell_area, widget,
636                                 "cellrendererfocus",
637                                 cell_area->x, cell_area->y,
638                                 cell_area->width, cell_area->height);
639                 }
640 }