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