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