]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrendererpixbuf.c
ec9890b435756425e700a7f49e268afbdf767e3f
[~andy/gtk] / gtk / gtkcellrendererpixbuf.c
1 /* gtkcellrendererpixbuf.c
2  * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include <stdlib.h>
22 #include "gtkcellrendererpixbuf.h"
23 #include "gtkiconfactory.h"
24 #include "gtkiconhelperprivate.h"
25 #include "gtkicontheme.h"
26 #include "gtkintl.h"
27 #include "gtkprivate.h"
28 #include "a11y/gtkimagecellaccessible.h"
29
30
31 /**
32  * SECTION:gtkcellrendererpixbuf
33  * @Short_description: Renders a pixbuf in a cell
34  * @Title: GtkCellRendererPixbuf
35  *
36  * A #GtkCellRendererPixbuf can be used to render an image in a cell. It allows
37  * to render either a given #GdkPixbuf (set via the
38  * #GtkCellRendererPixbuf:pixbuf property) or a stock icon (set via the
39  * #GtkCellRendererPixbuf:stock-id property).
40  *
41  * To support the tree view, #GtkCellRendererPixbuf also supports rendering two
42  * alternative pixbufs, when the #GtkCellRenderer:is-expander property is %TRUE.
43  * If the #GtkCellRenderer:is-expanded property is %TRUE and the
44  * #GtkCellRendererPixbuf:pixbuf-expander-open property is set to a pixbuf, it
45  * renders that pixbuf, if the #GtkCellRenderer:is-expanded property is %FALSE
46  * and the #GtkCellRendererPixbuf:pixbuf-expander-closed property is set to a
47  * pixbuf, it renders that one.
48  */
49
50
51 static void gtk_cell_renderer_pixbuf_get_property  (GObject                    *object,
52                                                     guint                       param_id,
53                                                     GValue                     *value,
54                                                     GParamSpec                 *pspec);
55 static void gtk_cell_renderer_pixbuf_set_property  (GObject                    *object,
56                                                     guint                       param_id,
57                                                     const GValue               *value,
58                                                     GParamSpec                 *pspec);
59 static void gtk_cell_renderer_pixbuf_get_size   (GtkCellRenderer            *cell,
60                                                  GtkWidget                  *widget,
61                                                  const GdkRectangle         *rectangle,
62                                                  gint                       *x_offset,
63                                                  gint                       *y_offset,
64                                                  gint                       *width,
65                                                  gint                       *height);
66 static void gtk_cell_renderer_pixbuf_render     (GtkCellRenderer            *cell,
67                                                  cairo_t                    *cr,
68                                                  GtkWidget                  *widget,
69                                                  const GdkRectangle         *background_area,
70                                                  const GdkRectangle         *cell_area,
71                                                  GtkCellRendererState        flags);
72
73
74 enum {
75   PROP_0,
76   PROP_PIXBUF,
77   PROP_PIXBUF_EXPANDER_OPEN,
78   PROP_PIXBUF_EXPANDER_CLOSED,
79   PROP_STOCK_ID,
80   PROP_STOCK_SIZE,
81   PROP_STOCK_DETAIL,
82   PROP_FOLLOW_STATE,
83   PROP_ICON_NAME,
84   PROP_GICON
85 };
86
87
88 struct _GtkCellRendererPixbufPrivate
89 {
90   GtkIconHelper *icon_helper;
91   GtkIconSize    icon_size;
92
93   GdkPixbuf *pixbuf_expander_open;
94   GdkPixbuf *pixbuf_expander_closed;
95
96   gboolean follow_state;
97
98   gchar *stock_detail;
99 };
100
101
102 G_DEFINE_TYPE (GtkCellRendererPixbuf, gtk_cell_renderer_pixbuf, GTK_TYPE_CELL_RENDERER)
103
104
105 static void
106 gtk_cell_renderer_pixbuf_init (GtkCellRendererPixbuf *cellpixbuf)
107 {
108   GtkCellRendererPixbufPrivate *priv;
109
110   cellpixbuf->priv = G_TYPE_INSTANCE_GET_PRIVATE (cellpixbuf,
111                                                   GTK_TYPE_CELL_RENDERER_PIXBUF,
112                                                   GtkCellRendererPixbufPrivate);
113   priv = cellpixbuf->priv;
114   priv->icon_helper = _gtk_icon_helper_new ();
115   priv->icon_size = GTK_ICON_SIZE_MENU;
116 }
117
118 static void
119 gtk_cell_renderer_pixbuf_finalize (GObject *object)
120 {
121   GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object);
122   GtkCellRendererPixbufPrivate *priv = cellpixbuf->priv;
123
124   g_clear_object (&priv->icon_helper);
125
126   if (priv->pixbuf_expander_open)
127     g_object_unref (priv->pixbuf_expander_open);
128   if (priv->pixbuf_expander_closed)
129     g_object_unref (priv->pixbuf_expander_closed);
130
131   g_free (priv->stock_detail);
132
133   G_OBJECT_CLASS (gtk_cell_renderer_pixbuf_parent_class)->finalize (object);
134 }
135
136 static void
137 gtk_cell_renderer_pixbuf_class_init (GtkCellRendererPixbufClass *class)
138 {
139   GObjectClass *object_class = G_OBJECT_CLASS (class);
140   GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
141
142   object_class->finalize = gtk_cell_renderer_pixbuf_finalize;
143
144   object_class->get_property = gtk_cell_renderer_pixbuf_get_property;
145   object_class->set_property = gtk_cell_renderer_pixbuf_set_property;
146
147   cell_class->get_size = gtk_cell_renderer_pixbuf_get_size;
148   cell_class->render = gtk_cell_renderer_pixbuf_render;
149
150   g_object_class_install_property (object_class,
151                                    PROP_PIXBUF,
152                                    g_param_spec_object ("pixbuf",
153                                                         P_("Pixbuf Object"),
154                                                         P_("The pixbuf to render"),
155                                                         GDK_TYPE_PIXBUF,
156                                                         GTK_PARAM_READWRITE));
157
158   g_object_class_install_property (object_class,
159                                    PROP_PIXBUF_EXPANDER_OPEN,
160                                    g_param_spec_object ("pixbuf-expander-open",
161                                                         P_("Pixbuf Expander Open"),
162                                                         P_("Pixbuf for open expander"),
163                                                         GDK_TYPE_PIXBUF,
164                                                         GTK_PARAM_READWRITE));
165
166   g_object_class_install_property (object_class,
167                                    PROP_PIXBUF_EXPANDER_CLOSED,
168                                    g_param_spec_object ("pixbuf-expander-closed",
169                                                         P_("Pixbuf Expander Closed"),
170                                                         P_("Pixbuf for closed expander"),
171                                                         GDK_TYPE_PIXBUF,
172                                                         GTK_PARAM_READWRITE));
173
174   g_object_class_install_property (object_class,
175                                    PROP_STOCK_ID,
176                                    g_param_spec_string ("stock-id",
177                                                         P_("Stock ID"),
178                                                         P_("The stock ID of the stock icon to render"),
179                                                         NULL,
180                                                         GTK_PARAM_READWRITE));
181
182   g_object_class_install_property (object_class,
183                                    PROP_STOCK_SIZE,
184                                    g_param_spec_uint ("stock-size",
185                                                       P_("Size"),
186                                                       P_("The GtkIconSize value that specifies the size of the rendered icon"),
187                                                       0,
188                                                       G_MAXUINT,
189                                                       GTK_ICON_SIZE_MENU,
190                                                       GTK_PARAM_READWRITE));
191
192   g_object_class_install_property (object_class,
193                                    PROP_STOCK_DETAIL,
194                                    g_param_spec_string ("stock-detail",
195                                                         P_("Detail"),
196                                                         P_("Render detail to pass to the theme engine"),
197                                                         NULL,
198                                                         GTK_PARAM_READWRITE));
199
200   
201   /**
202    * GtkCellRendererPixbuf:icon-name:
203    *
204    * The name of the themed icon to display.
205    * This property only has an effect if not overridden by "stock_id" 
206    * or "pixbuf" properties.
207    *
208    * Since: 2.8 
209    */
210   g_object_class_install_property (object_class,
211                                    PROP_ICON_NAME,
212                                    g_param_spec_string ("icon-name",
213                                                         P_("Icon Name"),
214                                                         P_("The name of the icon from the icon theme"),
215                                                         NULL,
216                                                         GTK_PARAM_READWRITE));
217
218   /**
219    * GtkCellRendererPixbuf:follow-state:
220    *
221    * Specifies whether the rendered pixbuf should be colorized
222    * according to the #GtkCellRendererState.
223    *
224    * Since: 2.8
225    */
226   g_object_class_install_property (object_class,
227                                    PROP_FOLLOW_STATE,
228                                    g_param_spec_boolean ("follow-state",
229                                                          P_("Follow State"),
230                                                          P_("Whether the rendered pixbuf should be "
231                                                             "colorized according to the state"),
232                                                          FALSE,
233                                                          GTK_PARAM_READWRITE));
234
235   /**
236    * GtkCellRendererPixbuf:gicon:
237    *
238    * The GIcon representing the icon to display.
239    * If the icon theme is changed, the image will be updated
240    * automatically.
241    *
242    * Since: 2.14
243    */
244   g_object_class_install_property (object_class,
245                                    PROP_GICON,
246                                    g_param_spec_object ("gicon",
247                                                         P_("Icon"),
248                                                         P_("The GIcon being displayed"),
249                                                         G_TYPE_ICON,
250                                                         GTK_PARAM_READWRITE));
251
252
253
254   g_type_class_add_private (object_class, sizeof (GtkCellRendererPixbufPrivate));
255
256   _gtk_cell_renderer_class_set_accessible_type (cell_class, GTK_TYPE_IMAGE_CELL_ACCESSIBLE);
257 }
258
259 static void
260 gtk_cell_renderer_pixbuf_get_property (GObject        *object,
261                                        guint           param_id,
262                                        GValue         *value,
263                                        GParamSpec     *pspec)
264 {
265   GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object);
266   GtkCellRendererPixbufPrivate *priv = cellpixbuf->priv;
267
268   switch (param_id)
269     {
270     case PROP_PIXBUF:
271       g_value_set_object (value, _gtk_icon_helper_peek_pixbuf (priv->icon_helper));
272       break;
273     case PROP_PIXBUF_EXPANDER_OPEN:
274       g_value_set_object (value, priv->pixbuf_expander_open);
275       break;
276     case PROP_PIXBUF_EXPANDER_CLOSED:
277       g_value_set_object (value, priv->pixbuf_expander_closed);
278       break;
279     case PROP_STOCK_ID:
280       g_value_set_string (value, _gtk_icon_helper_get_stock_id (priv->icon_helper));
281       break;
282     case PROP_STOCK_SIZE:
283       g_value_set_uint (value, priv->icon_size);
284       break;
285     case PROP_STOCK_DETAIL:
286       g_value_set_string (value, priv->stock_detail);
287       break;
288     case PROP_FOLLOW_STATE:
289       g_value_set_boolean (value, priv->follow_state);
290       break;
291     case PROP_ICON_NAME:
292       g_value_set_string (value, _gtk_icon_helper_get_icon_name (priv->icon_helper));
293       break;
294     case PROP_GICON:
295       g_value_set_object (value, _gtk_icon_helper_peek_gicon (priv->icon_helper));
296       break;
297     default:
298       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
299       break;
300     }
301 }
302
303 static void
304 gtk_cell_renderer_pixbuf_reset (GtkCellRendererPixbuf *cellpixbuf)
305 {
306   GtkCellRendererPixbufPrivate *priv = cellpixbuf->priv;
307   GtkImageType storage_type = _gtk_icon_helper_get_storage_type (priv->icon_helper);
308
309   switch (storage_type)
310     {
311     case GTK_IMAGE_PIXBUF:
312       g_object_notify (G_OBJECT (cellpixbuf), "pixbuf");
313       break;
314     case GTK_IMAGE_STOCK:
315       g_object_notify (G_OBJECT (cellpixbuf), "stock-id");      
316       break;
317     case GTK_IMAGE_ICON_NAME:
318       g_object_notify (G_OBJECT (cellpixbuf), "icon-name");
319       break;
320     case GTK_IMAGE_GICON:
321       g_object_notify (G_OBJECT (cellpixbuf), "gicon");
322       break;
323     case GTK_IMAGE_EMPTY:
324     default:
325       break;
326     }
327
328   _gtk_icon_helper_clear (priv->icon_helper);
329 }
330
331 static void
332 gtk_cell_renderer_pixbuf_set_property (GObject      *object,
333                                        guint         param_id,
334                                        const GValue *value,
335                                        GParamSpec   *pspec)
336 {
337   GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object);
338   GtkCellRendererPixbufPrivate *priv = cellpixbuf->priv;
339
340   switch (param_id)
341     {
342     case PROP_PIXBUF:
343       gtk_cell_renderer_pixbuf_reset (cellpixbuf);
344       _gtk_icon_helper_set_pixbuf (priv->icon_helper, g_value_get_object (value));
345       break;
346     case PROP_PIXBUF_EXPANDER_OPEN:
347       if (priv->pixbuf_expander_open)
348         g_object_unref (priv->pixbuf_expander_open);
349       priv->pixbuf_expander_open = (GdkPixbuf*) g_value_dup_object (value);
350       break;
351     case PROP_PIXBUF_EXPANDER_CLOSED:
352       if (priv->pixbuf_expander_closed)
353         g_object_unref (priv->pixbuf_expander_closed);
354       priv->pixbuf_expander_closed = (GdkPixbuf*) g_value_dup_object (value);
355       break;
356     case PROP_STOCK_ID:
357       gtk_cell_renderer_pixbuf_reset (cellpixbuf);
358       _gtk_icon_helper_set_stock_id (priv->icon_helper, g_value_get_string (value), priv->icon_size);
359       break;
360     case PROP_STOCK_SIZE:
361       priv->icon_size = g_value_get_uint (value);
362       _gtk_icon_helper_set_icon_size (priv->icon_helper, priv->icon_size);
363       break;
364     case PROP_STOCK_DETAIL:
365       g_free (priv->stock_detail);
366       priv->stock_detail = g_value_dup_string (value);
367       break;
368     case PROP_ICON_NAME:
369       gtk_cell_renderer_pixbuf_reset (cellpixbuf);
370       _gtk_icon_helper_set_icon_name (priv->icon_helper, g_value_get_string (value), priv->icon_size);
371       break;
372     case PROP_FOLLOW_STATE:
373       priv->follow_state = g_value_get_boolean (value);
374       break;
375     case PROP_GICON:
376       gtk_cell_renderer_pixbuf_reset (cellpixbuf);
377       _gtk_icon_helper_set_gicon (priv->icon_helper, g_value_get_object (value), priv->icon_size);
378       break;
379     default:
380       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
381       break;
382     }
383 }
384
385 /**
386  * gtk_cell_renderer_pixbuf_new:
387  * 
388  * Creates a new #GtkCellRendererPixbuf. Adjust rendering
389  * parameters using object properties. Object properties can be set
390  * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
391  * can bind a property to a value in a #GtkTreeModel. For example, you
392  * can bind the "pixbuf" property on the cell renderer to a pixbuf value
393  * in the model, thus rendering a different image in each row of the
394  * #GtkTreeView.
395  * 
396  * Return value: the new cell renderer
397  **/
398 GtkCellRenderer *
399 gtk_cell_renderer_pixbuf_new (void)
400 {
401   return g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF, NULL);
402 }
403
404 static void
405 gtk_cell_renderer_pixbuf_get_size (GtkCellRenderer    *cell,
406                                    GtkWidget          *widget,
407                                    const GdkRectangle *cell_area,
408                                    gint               *x_offset,
409                                    gint               *y_offset,
410                                    gint               *width,
411                                    gint               *height)
412 {
413   GtkCellRendererPixbuf *cellpixbuf = (GtkCellRendererPixbuf *) cell;
414   GtkCellRendererPixbufPrivate *priv = cellpixbuf->priv;
415   gint pixbuf_width  = 0;
416   gint pixbuf_height = 0;
417   gint calc_width;
418   gint calc_height;
419   gint xpad, ypad;
420
421   if (!_gtk_icon_helper_get_is_empty (priv->icon_helper))
422     _gtk_icon_helper_get_size (priv->icon_helper, 
423                                gtk_widget_get_style_context (widget),
424                                &pixbuf_width, &pixbuf_height);
425
426   if (priv->pixbuf_expander_open)
427     {
428       pixbuf_width  = MAX (pixbuf_width, gdk_pixbuf_get_width (priv->pixbuf_expander_open));
429       pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (priv->pixbuf_expander_open));
430     }
431   if (priv->pixbuf_expander_closed)
432     {
433       pixbuf_width  = MAX (pixbuf_width, gdk_pixbuf_get_width (priv->pixbuf_expander_closed));
434       pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (priv->pixbuf_expander_closed));
435     }
436
437   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
438   calc_width  = (gint) xpad * 2 + pixbuf_width;
439   calc_height = (gint) ypad * 2 + pixbuf_height;
440   
441   if (cell_area && pixbuf_width > 0 && pixbuf_height > 0)
442     {
443       gfloat xalign, yalign;
444
445       gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
446       if (x_offset)
447         {
448           *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
449                         (1.0 - xalign) : xalign) *
450                        (cell_area->width - calc_width));
451           *x_offset = MAX (*x_offset, 0);
452         }
453       if (y_offset)
454         {
455           *y_offset = (yalign *
456                        (cell_area->height - calc_height));
457           *y_offset = MAX (*y_offset, 0);
458         }
459     }
460   else
461     {
462       if (x_offset) *x_offset = 0;
463       if (y_offset) *y_offset = 0;
464     }
465
466   if (width)
467     *width = calc_width;
468   
469   if (height)
470     *height = calc_height;
471 }
472
473 static void
474 gtk_cell_renderer_pixbuf_render (GtkCellRenderer      *cell,
475                                  cairo_t              *cr,
476                                  GtkWidget            *widget,
477                                  const GdkRectangle   *background_area,
478                                  const GdkRectangle   *cell_area,
479                                  GtkCellRendererState  flags)
480
481 {
482   GtkCellRendererPixbuf *cellpixbuf = (GtkCellRendererPixbuf *) cell;
483   GtkCellRendererPixbufPrivate *priv = cellpixbuf->priv;
484   GtkStyleContext *context;
485   GdkRectangle pix_rect;
486   GdkRectangle draw_rect;
487   gboolean is_expander;
488   gint xpad, ypad;
489   GtkStateFlags state;
490   GtkIconHelper *icon_helper = NULL;
491
492   gtk_cell_renderer_pixbuf_get_size (cell, widget, (GdkRectangle *) cell_area,
493                                      &pix_rect.x, 
494                                      &pix_rect.y,
495                                      &pix_rect.width,
496                                      &pix_rect.height);
497
498   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
499   pix_rect.x += cell_area->x + xpad;
500   pix_rect.y += cell_area->y + ypad;
501   pix_rect.width -= xpad * 2;
502   pix_rect.height -= ypad * 2;
503
504   if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect))
505     return;
506
507   context = gtk_widget_get_style_context (widget);
508   gtk_style_context_save (context);
509
510   state = GTK_STATE_FLAG_NORMAL;
511
512   if (!gtk_widget_get_sensitive (widget) ||
513       !gtk_cell_renderer_get_sensitive (cell))
514     state |= GTK_STATE_FLAG_INSENSITIVE;
515   else if (priv->follow_state && 
516            (flags & (GTK_CELL_RENDERER_SELECTED |
517                      GTK_CELL_RENDERER_PRELIT)) != 0)
518     state = gtk_cell_renderer_get_state (cell, widget, flags);
519
520   gtk_style_context_set_state (context, state);
521
522   g_object_get (cell, "is-expander", &is_expander, NULL);
523   if (is_expander)
524     {
525       gboolean is_expanded;
526
527       g_object_get (cell, "is-expanded", &is_expanded, NULL);
528
529       if (is_expanded && priv->pixbuf_expander_open != NULL)
530         {
531           icon_helper = _gtk_icon_helper_new ();
532           _gtk_icon_helper_set_pixbuf (icon_helper, priv->pixbuf_expander_open);
533         }
534       else if (!is_expanded && priv->pixbuf_expander_closed != NULL)
535         {
536           icon_helper = _gtk_icon_helper_new ();
537           _gtk_icon_helper_set_pixbuf (icon_helper, priv->pixbuf_expander_closed);
538         }
539     }
540
541   if (icon_helper == NULL)
542     icon_helper = g_object_ref (priv->icon_helper);
543
544   _gtk_icon_helper_draw (icon_helper,
545                          context, cr,
546                          pix_rect.x, pix_rect.y);
547   g_object_unref (icon_helper);
548
549   gtk_style_context_restore (context);
550 }