]> Pileus Git - ~andy/gtk/blob - gtk/gtkiconhelper.c
ae1cb68091c7f99d7616514c49f30b3c79184a5a
[~andy/gtk] / gtk / gtkiconhelper.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2011 Red Hat, Inc.
3  *
4  * Authors: Cosimo Cecchi <cosimoc@gnome.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser 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  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser 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 "config.h"
23
24 #include "gtkiconhelperprivate.h"
25
26 G_DEFINE_TYPE (GtkIconHelper, _gtk_icon_helper, G_TYPE_OBJECT)
27
28 struct _GtkIconHelperPrivate {
29   GtkImageType storage_type;
30
31   GdkPixbuf *orig_pixbuf;
32   GdkPixbufAnimation *animation;
33   GIcon *gicon;
34   GtkIconSet *icon_set;
35   gchar *icon_name;
36   gchar *stock_id;
37
38   GtkIconSize icon_size;
39   gint pixel_size;
40
41   gboolean use_fallback;
42
43   GdkPixbuf *rendered_pixbuf;
44   GtkStateFlags last_rendered_state;
45 };
46
47 void
48 _gtk_icon_helper_clear (GtkIconHelper *self)
49 {
50   g_clear_object (&self->priv->gicon);
51   g_clear_object (&self->priv->orig_pixbuf);
52   g_clear_object (&self->priv->animation);
53   g_clear_object (&self->priv->rendered_pixbuf);
54
55   if (self->priv->icon_set != NULL)
56     {
57       gtk_icon_set_unref (self->priv->icon_set);
58       self->priv->icon_set = NULL;
59     }
60
61   g_free (self->priv->icon_name);
62   self->priv->icon_name = NULL;
63
64   g_free (self->priv->stock_id);
65   self->priv->stock_id = NULL;
66
67   self->priv->storage_type = GTK_IMAGE_EMPTY;
68   self->priv->icon_size = GTK_ICON_SIZE_INVALID;
69   self->priv->last_rendered_state = GTK_STATE_FLAG_NORMAL;
70 }
71
72 void
73 _gtk_icon_helper_invalidate (GtkIconHelper *self)
74 {
75   g_clear_object (&self->priv->rendered_pixbuf);
76 }
77
78 static void
79 gtk_icon_helper_finalize (GObject *object)
80 {
81   GtkIconHelper *self = GTK_ICON_HELPER (object);
82
83   _gtk_icon_helper_clear (self);
84   
85   G_OBJECT_CLASS (_gtk_icon_helper_parent_class)->finalize (object);
86 }
87
88 static void
89 _gtk_icon_helper_class_init (GtkIconHelperClass *klass)
90 {
91   GObjectClass *oclass;
92
93   oclass = G_OBJECT_CLASS (klass);
94   oclass->finalize = gtk_icon_helper_finalize;
95
96   g_type_class_add_private (klass, sizeof (GtkIconHelperPrivate));
97 }
98
99 static void
100 _gtk_icon_helper_init (GtkIconHelper *self)
101 {
102   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_ICON_HELPER, GtkIconHelperPrivate);
103
104   self->priv->storage_type = GTK_IMAGE_EMPTY;
105   self->priv->icon_size = GTK_ICON_SIZE_INVALID;
106   self->priv->pixel_size = -1;
107   self->priv->last_rendered_state = GTK_STATE_FLAG_NORMAL;
108 }
109
110 static void
111 ensure_icon_size (GtkIconHelper *self,
112                   GtkStyleContext *context,
113                   gint *width_out,
114                   gint *height_out)
115 {
116   gint width, height;
117   GtkSettings *settings;
118   GdkScreen *screen;
119
120   screen = gtk_style_context_get_screen (context);
121   settings = gtk_settings_get_for_screen (screen);
122
123   if (self->priv->pixel_size != -1)
124     {
125       width = height = self->priv->pixel_size;
126     }
127   else if (!gtk_icon_size_lookup_for_settings (settings,
128                                                self->priv->icon_size,
129                                                &width, &height))
130     {
131       if (self->priv->icon_size == GTK_ICON_SIZE_INVALID)
132         {
133           width = height = 0;
134         }
135       else
136         {
137           g_warning ("Invalid icon size %d\n", self->priv->icon_size);
138           width = height = 24;
139         }
140     }
141
142   *width_out = width;
143   *height_out = height;
144 }
145
146 static GdkPixbuf *
147 ensure_stated_icon_from_info (GtkIconHelper *self,
148                               GtkStyleContext *context,
149                               GtkIconInfo *info)
150 {
151   GdkPixbuf *destination = NULL;
152   gboolean symbolic;
153
154   symbolic = FALSE;
155
156   if (info)
157     destination =
158       gtk_icon_info_load_symbolic_for_context (info,
159                                                context,
160                                                &symbolic,
161                                                NULL);
162
163   if (destination == NULL)
164     {
165       GtkIconSet *icon_set;
166       icon_set = gtk_style_context_lookup_icon_set (context, GTK_STOCK_MISSING_IMAGE);
167
168       destination =
169         gtk_icon_set_render_icon_pixbuf (icon_set, context, self->priv->icon_size);
170     }
171   else if (!symbolic)
172     {
173       GtkIconSource *source;
174       GdkPixbuf *rendered;
175
176       source = gtk_icon_source_new ();
177       gtk_icon_source_set_pixbuf (source, destination);
178       /* The size here is arbitrary; since size isn't
179        * wildcarded in the source, it isn't supposed to be
180        * scaled by the engine function
181        */
182       gtk_icon_source_set_size (source,
183                                 GTK_ICON_SIZE_SMALL_TOOLBAR);
184       gtk_icon_source_set_size_wildcarded (source, FALSE);
185
186       rendered = gtk_render_icon_pixbuf (context, source, (GtkIconSize) -1);
187       gtk_icon_source_free (source);
188
189       g_object_unref (destination);
190       destination = rendered;
191     }
192
193   return destination;
194 }
195
196 static gboolean
197 check_invalidate_pixbuf (GtkIconHelper *self,
198                          GtkStyleContext *context)
199 {
200   GtkStateFlags state;
201
202   state = gtk_style_context_get_state (context);
203
204   if ((self->priv->rendered_pixbuf != NULL) &&
205       (self->priv->last_rendered_state == state))
206     return FALSE;
207
208   self->priv->last_rendered_state = state;
209   g_clear_object (&self->priv->rendered_pixbuf);
210
211   return TRUE;
212 }
213
214 static GtkIconLookupFlags
215 get_icon_lookup_flags (GtkIconHelper *self)
216 {
217   GtkIconLookupFlags flags = GTK_ICON_LOOKUP_USE_BUILTIN;
218
219   if (self->priv->use_fallback)
220     flags |= GTK_ICON_LOOKUP_GENERIC_FALLBACK;
221   if (self->priv->pixel_size != -1)
222     flags |= GTK_ICON_LOOKUP_FORCE_SIZE;
223
224   return flags;
225 }
226
227 static void
228 ensure_pixbuf_for_icon_name_or_gicon (GtkIconHelper *self,
229                                       GtkStyleContext *context)
230 {
231   GtkIconTheme *icon_theme;
232   gint width, height;
233   GtkIconInfo *info;
234   GtkIconLookupFlags flags;
235
236   if (!check_invalidate_pixbuf (self, context))
237     return;
238
239   icon_theme = gtk_icon_theme_get_default ();
240   flags = get_icon_lookup_flags (self);
241
242   ensure_icon_size (self, context, &width, &height);
243
244   if (self->priv->storage_type == GTK_IMAGE_ICON_NAME &&
245       self->priv->icon_name != NULL)
246     {
247       info = gtk_icon_theme_lookup_icon (icon_theme,
248                                          self->priv->icon_name,
249                                          MIN (width, height), flags);
250     }
251   else if (self->priv->storage_type == GTK_IMAGE_GICON &&
252            self->priv->gicon != NULL)
253     {
254       info = gtk_icon_theme_lookup_by_gicon (icon_theme,
255                                              self->priv->gicon,
256                                              MIN (width, height), flags);
257     }
258   else
259     {
260       g_assert_not_reached ();
261       return;
262     }
263
264   self->priv->rendered_pixbuf = ensure_stated_icon_from_info (self, context, info);
265
266   if (info)
267     gtk_icon_info_free (info);
268 }
269
270 static void
271 ensure_pixbuf_for_icon_set (GtkIconHelper *self,
272                             GtkStyleContext *context,
273                             GtkIconSet *icon_set)
274 {
275   if (!check_invalidate_pixbuf (self, context))
276     return;
277
278   self->priv->rendered_pixbuf = 
279     gtk_icon_set_render_icon_pixbuf (icon_set, context, self->priv->icon_size);
280 }
281
282 GdkPixbuf *
283 _gtk_icon_helper_ensure_pixbuf (GtkIconHelper *self,
284                                 GtkStyleContext *context)
285 {
286   GdkPixbuf *pixbuf = NULL;
287   GtkIconSet *icon_set;
288
289   switch (self->priv->storage_type)
290     {
291     case GTK_IMAGE_PIXBUF:
292       pixbuf = g_object_ref (self->priv->orig_pixbuf);
293       break;
294
295     case GTK_IMAGE_STOCK:
296       icon_set = gtk_style_context_lookup_icon_set (context, self->priv->stock_id);
297       ensure_pixbuf_for_icon_set (self, context, icon_set);
298       break;
299
300     case GTK_IMAGE_ICON_SET:
301       icon_set = self->priv->icon_set;
302       ensure_pixbuf_for_icon_set (self, context, icon_set);
303       break;
304
305     case GTK_IMAGE_ICON_NAME:
306     case GTK_IMAGE_GICON:
307       ensure_pixbuf_for_icon_name_or_gicon (self, context);
308       break;
309
310     case GTK_IMAGE_ANIMATION:
311     case GTK_IMAGE_EMPTY:
312     default:
313       pixbuf = NULL;
314       break;
315     }
316
317   if (pixbuf == NULL &&
318       self->priv->rendered_pixbuf != NULL)
319     pixbuf = g_object_ref (self->priv->rendered_pixbuf);
320
321   return pixbuf;
322 }
323
324 void
325 _gtk_icon_helper_get_size (GtkIconHelper *self,
326                            GtkStyleContext *context,
327                            gint *width_out,
328                            gint *height_out)
329 {
330   GdkPixbuf *pix;
331   gint width, height;
332
333   width = height = 0;
334   pix = _gtk_icon_helper_ensure_pixbuf (self, context);
335
336   if (pix != NULL)
337     {
338       width = gdk_pixbuf_get_width (pix);
339       height = gdk_pixbuf_get_height (pix);
340
341       g_object_unref (pix);
342     }
343   else if (self->priv->storage_type == GTK_IMAGE_ANIMATION)
344     {
345       width = gdk_pixbuf_animation_get_width (self->priv->animation);
346       height = gdk_pixbuf_animation_get_height (self->priv->animation);
347     }
348   else if (self->priv->icon_size != -1)
349     {
350       ensure_icon_size (self, context, &width, &height);
351     }
352
353   if (width_out)
354     *width_out = width;
355   if (height_out)
356     *height_out = height;
357 }
358
359 void 
360 _gtk_icon_helper_set_gicon (GtkIconHelper *self,
361                             GIcon *gicon,
362                             GtkIconSize icon_size)
363 {
364   _gtk_icon_helper_clear (self);
365
366   if (gicon != NULL)
367     {
368       self->priv->storage_type = GTK_IMAGE_GICON;
369       self->priv->gicon = g_object_ref (gicon);
370       _gtk_icon_helper_set_icon_size (self, icon_size);
371     }
372 }
373
374 void 
375 _gtk_icon_helper_set_icon_name (GtkIconHelper *self,
376                                 const gchar *icon_name,
377                                 GtkIconSize icon_size)
378 {
379   _gtk_icon_helper_clear (self);
380
381   if (icon_name != NULL &&
382       g_strcmp0 (icon_name, "") != 0)
383     {
384       self->priv->storage_type = GTK_IMAGE_ICON_NAME;
385       self->priv->icon_name = g_strdup (icon_name);
386       _gtk_icon_helper_set_icon_size (self, icon_size);
387     }
388 }
389
390 void 
391 _gtk_icon_helper_set_icon_set (GtkIconHelper *self,
392                                GtkIconSet *icon_set,
393                                GtkIconSize icon_size)
394 {
395   _gtk_icon_helper_clear (self);
396
397   if (icon_set != NULL)
398     {
399       self->priv->storage_type = GTK_IMAGE_ICON_SET;
400       self->priv->icon_set = gtk_icon_set_ref (icon_set);
401       _gtk_icon_helper_set_icon_size (self, icon_size);
402     }
403 }
404
405 void 
406 _gtk_icon_helper_set_pixbuf (GtkIconHelper *self,
407                              GdkPixbuf *pixbuf)
408 {
409   _gtk_icon_helper_clear (self);
410
411   if (pixbuf != NULL)
412     {
413       self->priv->storage_type = GTK_IMAGE_PIXBUF;
414       self->priv->orig_pixbuf = g_object_ref (pixbuf);
415     }
416 }
417
418 void 
419 _gtk_icon_helper_set_animation (GtkIconHelper *self,
420                                 GdkPixbufAnimation *animation)
421 {
422   _gtk_icon_helper_clear (self);
423
424   if (animation != NULL)
425     {
426       self->priv->storage_type = GTK_IMAGE_ANIMATION;
427       self->priv->animation = g_object_ref (animation);
428     }
429 }
430
431 void 
432 _gtk_icon_helper_set_stock_id (GtkIconHelper *self,
433                                const gchar *stock_id,
434                                GtkIconSize icon_size)
435 {
436   _gtk_icon_helper_clear (self);
437
438   if (stock_id != NULL)
439     {
440       self->priv->storage_type = GTK_IMAGE_STOCK;
441       self->priv->stock_id = g_strdup (stock_id);
442       _gtk_icon_helper_set_icon_size (self, icon_size);
443     }
444 }
445
446 void 
447 _gtk_icon_helper_set_icon_size (GtkIconHelper *self,
448                                 GtkIconSize icon_size)
449 {
450   if (self->priv->icon_size != icon_size)
451     {
452       self->priv->icon_size = icon_size;
453       _gtk_icon_helper_invalidate (self);
454     }
455 }
456
457 void 
458 _gtk_icon_helper_set_pixel_size (GtkIconHelper *self,
459                                  gint pixel_size)
460 {
461   if (self->priv->pixel_size != pixel_size)
462     {
463       self->priv->pixel_size = pixel_size;
464       _gtk_icon_helper_invalidate (self);
465     }
466 }
467
468 void 
469 _gtk_icon_helper_set_use_fallback (GtkIconHelper *self,
470                                    gboolean use_fallback)
471 {
472   if (self->priv->use_fallback != use_fallback)
473     {
474       self->priv->use_fallback = use_fallback;
475       _gtk_icon_helper_invalidate (self);
476     }
477 }
478
479 GtkImageType
480 _gtk_icon_helper_get_storage_type (GtkIconHelper *self)
481 {
482   return self->priv->storage_type;
483 }
484
485 gboolean
486 _gtk_icon_helper_get_use_fallback (GtkIconHelper *self)
487 {
488   return self->priv->use_fallback;
489 }
490
491 GtkIconSize
492 _gtk_icon_helper_get_icon_size (GtkIconHelper *self)
493 {
494   return self->priv->icon_size;
495 }
496
497 gint
498 _gtk_icon_helper_get_pixel_size (GtkIconHelper *self)
499 {
500   return self->priv->pixel_size;
501 }
502
503 GdkPixbuf *
504 _gtk_icon_helper_peek_pixbuf (GtkIconHelper *self)
505 {
506   return self->priv->orig_pixbuf;
507 }
508
509 GIcon *
510 _gtk_icon_helper_peek_gicon (GtkIconHelper *self)
511 {
512   return self->priv->gicon;
513 }
514
515 GdkPixbufAnimation *
516 _gtk_icon_helper_peek_animation (GtkIconHelper *self)
517 {
518   return self->priv->animation;
519 }
520
521 GtkIconSet *
522 _gtk_icon_helper_peek_icon_set (GtkIconHelper *self)
523 {
524   return self->priv->icon_set;
525 }
526
527 const gchar *
528 _gtk_icon_helper_get_stock_id (GtkIconHelper *self)
529 {
530   return self->priv->stock_id;
531 }
532
533 const gchar *
534 _gtk_icon_helper_get_icon_name (GtkIconHelper *self)
535 {
536   return self->priv->icon_name;
537 }
538
539 GtkIconHelper *
540 _gtk_icon_helper_new (void)
541 {
542   return g_object_new (GTK_TYPE_ICON_HELPER, NULL);
543 }
544
545 void
546 _gtk_icon_helper_draw (GtkIconHelper *self,
547                        GtkStyleContext *context,
548                        cairo_t *cr,
549                        gdouble x,
550                        gdouble y)
551 {
552   GdkPixbuf *pixbuf;
553
554   pixbuf = _gtk_icon_helper_ensure_pixbuf (self, context);
555
556   if (pixbuf != NULL)
557     {
558       gtk_render_icon (context, cr, pixbuf, x, y);
559       g_object_unref (pixbuf);
560     }
561 }
562
563 gboolean
564 _gtk_icon_helper_get_is_empty (GtkIconHelper *self)
565 {
566   return (self->priv->storage_type == GTK_IMAGE_EMPTY);
567 }