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, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 
22 #include "gtkiconhelperprivate.h"
23 
24 #include <math.h>
25 
26 #include "gtkcssenumvalueprivate.h"
27 #include "gtkcssiconthemevalueprivate.h"
28 #include "gtkcssnodeprivate.h"
29 #include "gtkcssstyleprivate.h"
30 #include "gtkcssstylepropertyprivate.h"
31 #include "gtkcsstransientnodeprivate.h"
32 #include "gtkiconthemeprivate.h"
33 #include "gtkrendericonprivate.h"
34 #include "deprecated/gtkiconfactoryprivate.h"
35 #include "deprecated/gtkstock.h"
36 
37 struct _GtkIconHelperPrivate {
38   GtkImageDefinition *def;
39 
40   GtkIconSize icon_size;
41   gint pixel_size;
42 
43   guint use_fallback : 1;
44   guint force_scale_pixbuf : 1;
45   guint rendered_surface_is_symbolic : 1;
46 
47   cairo_surface_t *rendered_surface;
48 };
49 
G_DEFINE_TYPE_WITH_PRIVATE(GtkIconHelper,gtk_icon_helper,GTK_TYPE_CSS_GADGET)50 G_DEFINE_TYPE_WITH_PRIVATE (GtkIconHelper, gtk_icon_helper, GTK_TYPE_CSS_GADGET)
51 
52 static void
53 gtk_icon_helper_invalidate (GtkIconHelper *self)
54 {
55   if (self->priv->rendered_surface != NULL)
56     {
57       cairo_surface_destroy (self->priv->rendered_surface);
58       self->priv->rendered_surface = NULL;
59       self->priv->rendered_surface_is_symbolic = FALSE;
60     }
61 
62   if (!GTK_IS_CSS_TRANSIENT_NODE (gtk_css_gadget_get_node (GTK_CSS_GADGET (self))))
63     gtk_widget_queue_resize (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self)));
64 }
65 
66 void
gtk_icon_helper_invalidate_for_change(GtkIconHelper * self,GtkCssStyleChange * change)67 gtk_icon_helper_invalidate_for_change (GtkIconHelper     *self,
68                                        GtkCssStyleChange *change)
69 {
70   GtkIconHelperPrivate *priv = self->priv;
71 
72   if (change == NULL ||
73       ((gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_SYMBOLIC_ICON) &&
74         priv->rendered_surface_is_symbolic) ||
75        (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_ICON) &&
76         !priv->rendered_surface_is_symbolic)))
77     {
78       gtk_icon_helper_invalidate (self);
79     }
80 }
81 
82 static void
gtk_icon_helper_take_definition(GtkIconHelper * self,GtkImageDefinition * def)83 gtk_icon_helper_take_definition (GtkIconHelper      *self,
84                                  GtkImageDefinition *def)
85 {
86   _gtk_icon_helper_clear (self);
87 
88   if (def == NULL)
89     return;
90 
91   gtk_image_definition_unref (self->priv->def);
92   self->priv->def = def;
93 
94   gtk_icon_helper_invalidate (self);
95 }
96 
97 void
_gtk_icon_helper_clear(GtkIconHelper * self)98 _gtk_icon_helper_clear (GtkIconHelper *self)
99 {
100   g_clear_pointer (&self->priv->rendered_surface, cairo_surface_destroy);
101 
102   gtk_image_definition_unref (self->priv->def);
103   self->priv->def = gtk_image_definition_new_empty ();
104 
105   self->priv->icon_size = GTK_ICON_SIZE_INVALID;
106 
107   gtk_icon_helper_invalidate (self);
108 }
109 
110 static void
gtk_icon_helper_get_preferred_size(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)111 gtk_icon_helper_get_preferred_size (GtkCssGadget   *gadget,
112                                     GtkOrientation  orientation,
113                                     gint            for_size,
114                                     gint           *minimum,
115                                     gint           *natural,
116                                     gint           *minimum_baseline,
117                                     gint           *natural_baseline)
118 {
119   GtkIconHelper *self = GTK_ICON_HELPER (gadget);
120   int icon_width, icon_height;
121 
122   _gtk_icon_helper_get_size (self, &icon_width, &icon_height);
123 
124   if (orientation == GTK_ORIENTATION_HORIZONTAL)
125     *minimum = *natural = icon_width;
126   else
127     *minimum = *natural = icon_height;
128 }
129 
130 static void
gtk_icon_helper_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip)131 gtk_icon_helper_allocate (GtkCssGadget        *gadget,
132                           const GtkAllocation *allocation,
133                           int                  baseline,
134                           GtkAllocation       *out_clip)
135 {
136   GTK_CSS_GADGET_CLASS (gtk_icon_helper_parent_class)->allocate (gadget, allocation, baseline, out_clip);
137 }
138 
139 static gboolean
gtk_icon_helper_draw(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height)140 gtk_icon_helper_draw (GtkCssGadget *gadget,
141                       cairo_t      *cr,
142                       int           x,
143                       int           y,
144                       int           width,
145                       int           height)
146 {
147   GtkIconHelper *self = GTK_ICON_HELPER (gadget);
148   int icon_width, icon_height;
149 
150   _gtk_icon_helper_get_size (self, &icon_width, &icon_height);
151   _gtk_icon_helper_draw (self,
152                          cr,
153                          x + (width - icon_width) / 2,
154                          y + (height - icon_height) / 2);
155 
156   return FALSE;
157 }
158 
159 static void
gtk_icon_helper_style_changed(GtkCssGadget * gadget,GtkCssStyleChange * change)160 gtk_icon_helper_style_changed (GtkCssGadget      *gadget,
161                                GtkCssStyleChange *change)
162 {
163   gtk_icon_helper_invalidate_for_change (GTK_ICON_HELPER (gadget), change);
164 
165   if (!GTK_IS_CSS_TRANSIENT_NODE (gtk_css_gadget_get_node (gadget)))
166     GTK_CSS_GADGET_CLASS (gtk_icon_helper_parent_class)->style_changed (gadget, change);
167 }
168 
169 static void
gtk_icon_helper_constructed(GObject * object)170 gtk_icon_helper_constructed (GObject *object)
171 {
172   GtkIconHelper *self = GTK_ICON_HELPER (object);
173   GtkWidget *widget;
174 
175   widget = gtk_css_gadget_get_owner (GTK_CSS_GADGET (self));
176 
177   g_signal_connect_swapped (widget, "direction-changed", G_CALLBACK (gtk_icon_helper_invalidate), self);
178   g_signal_connect_swapped (widget, "notify::scale-factor", G_CALLBACK (gtk_icon_helper_invalidate), self);
179 
180   G_OBJECT_CLASS (gtk_icon_helper_parent_class)->constructed (object);
181 }
182 
183 static void
gtk_icon_helper_finalize(GObject * object)184 gtk_icon_helper_finalize (GObject *object)
185 {
186   GtkIconHelper *self = GTK_ICON_HELPER (object);
187   GtkWidget *widget;
188 
189   widget = gtk_css_gadget_get_owner (GTK_CSS_GADGET (self));
190   g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (gtk_icon_helper_invalidate), self);
191 
192   _gtk_icon_helper_clear (self);
193   gtk_image_definition_unref (self->priv->def);
194 
195   G_OBJECT_CLASS (gtk_icon_helper_parent_class)->finalize (object);
196 }
197 
198 static void
gtk_icon_helper_class_init(GtkIconHelperClass * klass)199 gtk_icon_helper_class_init (GtkIconHelperClass *klass)
200 {
201   GtkCssGadgetClass *gadget_class = GTK_CSS_GADGET_CLASS (klass);
202   GObjectClass *object_class = G_OBJECT_CLASS (klass);
203 
204   gadget_class->get_preferred_size = gtk_icon_helper_get_preferred_size;
205   gadget_class->allocate = gtk_icon_helper_allocate;
206   gadget_class->draw = gtk_icon_helper_draw;
207   gadget_class->style_changed = gtk_icon_helper_style_changed;
208 
209   object_class->constructed = gtk_icon_helper_constructed;
210   object_class->finalize = gtk_icon_helper_finalize;
211 }
212 
213 static void
gtk_icon_helper_init(GtkIconHelper * self)214 gtk_icon_helper_init (GtkIconHelper *self)
215 {
216   self->priv = gtk_icon_helper_get_instance_private (self);
217 
218   self->priv->def = gtk_image_definition_new_empty ();
219 
220   self->priv->icon_size = GTK_ICON_SIZE_INVALID;
221   self->priv->pixel_size = -1;
222   self->priv->rendered_surface_is_symbolic = FALSE;
223 }
224 
225 static void
ensure_icon_size(GtkIconHelper * self,gint * width_out,gint * height_out)226 ensure_icon_size (GtkIconHelper *self,
227 		  gint *width_out,
228 		  gint *height_out)
229 {
230   gint width, height;
231 
232   if (self->priv->pixel_size != -1)
233     {
234       width = height = self->priv->pixel_size;
235     }
236   else if (!gtk_icon_size_lookup (self->priv->icon_size, &width, &height))
237     {
238       if (self->priv->icon_size == GTK_ICON_SIZE_INVALID)
239         {
240           width = height = 0;
241         }
242       else
243         {
244           g_warning ("Invalid icon size %d", self->priv->icon_size);
245           width = height = 24;
246         }
247     }
248 
249   *width_out = width;
250   *height_out = height;
251 }
252 
253 static GtkIconLookupFlags
get_icon_lookup_flags(GtkIconHelper * self,GtkCssStyle * style,GtkTextDirection dir)254 get_icon_lookup_flags (GtkIconHelper    *self,
255                        GtkCssStyle      *style,
256                        GtkTextDirection  dir)
257 {
258   GtkIconLookupFlags flags;
259   GtkCssIconStyle icon_style;
260 
261   flags = GTK_ICON_LOOKUP_USE_BUILTIN;
262 
263   if (self->priv->pixel_size != -1 || self->priv->force_scale_pixbuf)
264     flags |= GTK_ICON_LOOKUP_FORCE_SIZE;
265 
266   icon_style = _gtk_css_icon_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_STYLE));
267 
268   switch (icon_style)
269     {
270     case GTK_CSS_ICON_STYLE_REGULAR:
271       flags |= GTK_ICON_LOOKUP_FORCE_REGULAR;
272       break;
273     case GTK_CSS_ICON_STYLE_SYMBOLIC:
274       flags |= GTK_ICON_LOOKUP_FORCE_SYMBOLIC;
275       break;
276     case GTK_CSS_ICON_STYLE_REQUESTED:
277       break;
278     default:
279       g_assert_not_reached ();
280       return 0;
281     }
282 
283   if (dir == GTK_TEXT_DIR_LTR)
284     flags |= GTK_ICON_LOOKUP_DIR_LTR;
285   else if (dir == GTK_TEXT_DIR_RTL)
286     flags |= GTK_ICON_LOOKUP_DIR_RTL;
287 
288   return flags;
289 }
290 
291 static void
get_surface_size(GtkIconHelper * self,cairo_surface_t * surface,int * width,int * height)292 get_surface_size (GtkIconHelper   *self,
293 		  cairo_surface_t *surface,
294 		  int *width,
295 		  int *height)
296 {
297   GdkRectangle clip;
298   cairo_t *cr;
299 
300   cr = cairo_create (surface);
301   if (gdk_cairo_get_clip_rectangle (cr, &clip))
302     {
303       if (clip.x != 0 || clip.y != 0)
304         {
305           g_warning ("origin of surface is %d %d, not supported", clip.x, clip.y);
306         }
307       *width = clip.width;
308       *height = clip.height;
309     }
310   else
311     {
312       g_warning ("infinite surface size not supported");
313       ensure_icon_size (self, width, height);
314     }
315 
316   cairo_destroy (cr);
317 }
318 
319 static cairo_surface_t *
ensure_surface_from_surface(GtkIconHelper * self,cairo_surface_t * orig_surface)320 ensure_surface_from_surface (GtkIconHelper   *self,
321                              cairo_surface_t *orig_surface)
322 {
323   return cairo_surface_reference (orig_surface);
324 }
325 
326 static gboolean
get_pixbuf_size(GtkIconHelper * self,gint scale,GdkPixbuf * orig_pixbuf,gint orig_scale,gint * width_out,gint * height_out,gint * scale_out)327 get_pixbuf_size (GtkIconHelper   *self,
328                  gint             scale,
329                  GdkPixbuf       *orig_pixbuf,
330                  gint             orig_scale,
331                  gint *width_out,
332                  gint *height_out,
333                  gint *scale_out)
334 {
335   gboolean scale_pixmap;
336   gint width, height;
337 
338   scale_pixmap = FALSE;
339 
340   if (self->priv->force_scale_pixbuf &&
341       (self->priv->pixel_size != -1 ||
342        self->priv->icon_size != GTK_ICON_SIZE_INVALID))
343     {
344       ensure_icon_size (self, &width, &height);
345 
346       if (scale != orig_scale ||
347 	  width < gdk_pixbuf_get_width (orig_pixbuf) / orig_scale ||
348           height < gdk_pixbuf_get_height (orig_pixbuf) / orig_scale)
349 	{
350 	  width = MIN (width * scale, gdk_pixbuf_get_width (orig_pixbuf) * scale / orig_scale);
351 	  height = MIN (height * scale, gdk_pixbuf_get_height (orig_pixbuf) * scale / orig_scale);
352 
353           scale_pixmap = TRUE;
354 	}
355       else
356 	{
357 	  width = gdk_pixbuf_get_width (orig_pixbuf);
358 	  height = gdk_pixbuf_get_height (orig_pixbuf);
359 	  scale = orig_scale;
360 	}
361     }
362   else
363     {
364       width = gdk_pixbuf_get_width (orig_pixbuf);
365       height = gdk_pixbuf_get_height (orig_pixbuf);
366       scale = orig_scale;
367     }
368 
369   *width_out = width;
370   *height_out = height;
371   *scale_out = scale;
372 
373   return scale_pixmap;
374 }
375 
376 static cairo_surface_t *
ensure_surface_from_pixbuf(GtkIconHelper * self,GtkCssStyle * style,gint scale,GdkPixbuf * orig_pixbuf,gint orig_scale)377 ensure_surface_from_pixbuf (GtkIconHelper *self,
378                             GtkCssStyle   *style,
379                             gint           scale,
380                             GdkPixbuf     *orig_pixbuf,
381                             gint           orig_scale)
382 {
383   gint width, height;
384   cairo_surface_t *surface;
385   GdkPixbuf *pixbuf;
386   GtkCssIconEffect icon_effect;
387 
388   if (get_pixbuf_size (self,
389                        scale,
390                        orig_pixbuf,
391                        orig_scale,
392                        &width, &height, &scale))
393     pixbuf = gdk_pixbuf_scale_simple (orig_pixbuf,
394                                       width, height,
395                                       GDK_INTERP_BILINEAR);
396   else
397     pixbuf = g_object_ref (orig_pixbuf);
398 
399   surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, gtk_widget_get_window (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self))));
400   icon_effect = _gtk_css_icon_effect_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_EFFECT));
401   gtk_css_icon_effect_apply (icon_effect, surface);
402   g_object_unref (pixbuf);
403 
404   return surface;
405 }
406 
407 static cairo_surface_t *
ensure_surface_for_icon_set(GtkIconHelper * self,GtkCssStyle * style,GtkTextDirection direction,gint scale,GtkIconSet * icon_set)408 ensure_surface_for_icon_set (GtkIconHelper    *self,
409                              GtkCssStyle      *style,
410                              GtkTextDirection  direction,
411                              gint              scale,
412 			     GtkIconSet       *icon_set)
413 {
414   cairo_surface_t *surface;
415   GdkPixbuf *pixbuf;
416 
417   pixbuf = gtk_icon_set_render_icon_pixbuf_for_scale (icon_set,
418                                                       style,
419                                                       direction,
420                                                       self->priv->icon_size,
421                                                       scale);
422   surface = gdk_cairo_surface_create_from_pixbuf (pixbuf,
423                                                   scale,
424                                                   gtk_widget_get_window (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self))));
425   g_object_unref (pixbuf);
426 
427   return surface;
428 }
429 
430 static cairo_surface_t *
ensure_surface_for_gicon(GtkIconHelper * self,GtkCssStyle * style,GtkTextDirection dir,gint scale,GIcon * gicon)431 ensure_surface_for_gicon (GtkIconHelper    *self,
432                           GtkCssStyle      *style,
433                           GtkTextDirection  dir,
434                           gint              scale,
435                           GIcon            *gicon)
436 {
437   GtkIconHelperPrivate *priv = self->priv;
438   GtkIconTheme *icon_theme;
439   gint width, height;
440   GtkIconInfo *info;
441   GtkIconLookupFlags flags;
442   cairo_surface_t *surface;
443   GdkPixbuf *destination;
444   gboolean symbolic;
445 
446   icon_theme = gtk_css_icon_theme_value_get_icon_theme
447     (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_THEME));
448   flags = get_icon_lookup_flags (self, style, dir);
449 
450   ensure_icon_size (self, &width, &height);
451 
452   info = gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme,
453                                                    gicon,
454                                                    MIN (width, height),
455                                                    scale, flags);
456   if (info)
457     {
458       symbolic = gtk_icon_info_is_symbolic (info);
459 
460       if (symbolic)
461         {
462           GdkRGBA fg, success_color, warning_color, error_color;
463 
464           gtk_icon_theme_lookup_symbolic_colors (style, &fg, &success_color, &warning_color, &error_color);
465 
466           destination = gtk_icon_info_load_symbolic (info,
467                                                      &fg, &success_color,
468                                                      &warning_color, &error_color,
469                                                      NULL,
470                                                      NULL);
471         }
472       else
473         {
474           destination = gtk_icon_info_load_icon (info, NULL);
475         }
476 
477       g_object_unref (info);
478     }
479   else
480     {
481       destination = NULL;
482     }
483 
484   if (destination == NULL)
485     {
486       GError *error = NULL;
487       destination = gtk_icon_theme_load_icon (icon_theme,
488                                               "image-missing",
489                                               width,
490                                               flags | GTK_ICON_LOOKUP_USE_BUILTIN | GTK_ICON_LOOKUP_GENERIC_FALLBACK,
491                                               &error);
492       /* We include this image as resource, so we always have it available or
493        * the icontheme code is broken */
494       g_assert_no_error (error);
495       g_assert (destination);
496       symbolic = FALSE;
497     }
498 
499   surface = gdk_cairo_surface_create_from_pixbuf (destination, scale, gtk_widget_get_window (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self))));
500 
501   if (!symbolic)
502     {
503       GtkCssIconEffect icon_effect;
504 
505       icon_effect = _gtk_css_icon_effect_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_EFFECT));
506       gtk_css_icon_effect_apply (icon_effect, surface);
507     }
508   else
509     {
510       priv->rendered_surface_is_symbolic = TRUE;
511     }
512 
513   g_object_unref (destination);
514 
515   return surface;
516 }
517 
518 cairo_surface_t *
gtk_icon_helper_load_surface(GtkIconHelper * self,int scale)519 gtk_icon_helper_load_surface (GtkIconHelper   *self,
520                               int              scale)
521 {
522   cairo_surface_t *surface;
523   GtkIconSet *icon_set;
524   GIcon *gicon;
525 
526   switch (gtk_image_definition_get_storage_type (self->priv->def))
527     {
528     case GTK_IMAGE_SURFACE:
529       surface = ensure_surface_from_surface (self, gtk_image_definition_get_surface (self->priv->def));
530       break;
531 
532     case GTK_IMAGE_PIXBUF:
533       surface = ensure_surface_from_pixbuf (self,
534                                             gtk_css_node_get_style (gtk_css_gadget_get_node (GTK_CSS_GADGET (self))),
535                                             scale,
536                                             gtk_image_definition_get_pixbuf (self->priv->def),
537                                             gtk_image_definition_get_scale (self->priv->def));
538       break;
539 
540     case GTK_IMAGE_STOCK:
541 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
542       icon_set = gtk_icon_factory_lookup_default (gtk_image_definition_get_stock (self->priv->def));
543 G_GNUC_END_IGNORE_DEPRECATIONS;
544       if (icon_set != NULL)
545 	surface = ensure_surface_for_icon_set (self,
546                                                gtk_css_node_get_style (gtk_css_gadget_get_node (GTK_CSS_GADGET (self))),
547                                                gtk_widget_get_direction (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self))),
548                                                scale, icon_set);
549       else
550 	surface = NULL;
551       break;
552 
553     case GTK_IMAGE_ICON_SET:
554       icon_set = gtk_image_definition_get_icon_set (self->priv->def);
555       surface = ensure_surface_for_icon_set (self,
556                                              gtk_css_node_get_style (gtk_css_gadget_get_node (GTK_CSS_GADGET (self))),
557                                              gtk_widget_get_direction (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self))),
558                                              scale, icon_set);
559       break;
560 
561     case GTK_IMAGE_ICON_NAME:
562       if (self->priv->use_fallback)
563         gicon = g_themed_icon_new_with_default_fallbacks (gtk_image_definition_get_icon_name (self->priv->def));
564       else
565         gicon = g_themed_icon_new (gtk_image_definition_get_icon_name (self->priv->def));
566       surface = ensure_surface_for_gicon (self,
567                                           gtk_css_node_get_style (gtk_css_gadget_get_node (GTK_CSS_GADGET (self))),
568                                           gtk_widget_get_direction (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self))),
569                                           scale,
570                                           gicon);
571       g_object_unref (gicon);
572       break;
573 
574     case GTK_IMAGE_GICON:
575       surface = ensure_surface_for_gicon (self,
576                                           gtk_css_node_get_style (gtk_css_gadget_get_node (GTK_CSS_GADGET (self))),
577                                           gtk_widget_get_direction (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self))),
578                                           scale,
579                                           gtk_image_definition_get_gicon (self->priv->def));
580       break;
581 
582     case GTK_IMAGE_ANIMATION:
583     case GTK_IMAGE_EMPTY:
584     default:
585       surface = NULL;
586       break;
587     }
588 
589   return surface;
590 
591 }
592 
593 static void
gtk_icon_helper_ensure_surface(GtkIconHelper * self)594 gtk_icon_helper_ensure_surface (GtkIconHelper *self)
595 {
596   int scale;
597 
598   if (self->priv->rendered_surface)
599     return;
600 
601   scale = gtk_widget_get_scale_factor (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self)));
602 
603   self->priv->rendered_surface = gtk_icon_helper_load_surface (self, scale);
604 }
605 
606 void
_gtk_icon_helper_get_size(GtkIconHelper * self,gint * width_out,gint * height_out)607 _gtk_icon_helper_get_size (GtkIconHelper *self,
608                            gint *width_out,
609                            gint *height_out)
610 {
611   gint width, height, scale;
612 
613   width = height = 0;
614 
615   /* Certain kinds of images are easy to calculate the size for, these
616      we do immediately to avoid having to potentially load the image
617      data for something that may not yet be visible */
618   switch (gtk_image_definition_get_storage_type (self->priv->def))
619     {
620     case GTK_IMAGE_SURFACE:
621       get_surface_size (self,
622                         gtk_image_definition_get_surface (self->priv->def),
623                         &width,
624                         &height);
625       break;
626 
627     case GTK_IMAGE_PIXBUF:
628       get_pixbuf_size (self,
629                        gtk_widget_get_scale_factor (gtk_css_gadget_get_owner (GTK_CSS_GADGET (self))),
630                        gtk_image_definition_get_pixbuf (self->priv->def),
631                        gtk_image_definition_get_scale (self->priv->def),
632                        &width, &height, &scale);
633       width = (width + scale - 1) / scale;
634       height = (height + scale - 1) / scale;
635       break;
636 
637     case GTK_IMAGE_ANIMATION:
638       {
639         GdkPixbufAnimation *animation = gtk_image_definition_get_animation (self->priv->def);
640         width = gdk_pixbuf_animation_get_width (animation);
641         height = gdk_pixbuf_animation_get_height (animation);
642         break;
643       }
644 
645     case GTK_IMAGE_ICON_NAME:
646     case GTK_IMAGE_GICON:
647       if (self->priv->pixel_size != -1 || self->priv->force_scale_pixbuf)
648         ensure_icon_size (self, &width, &height);
649 
650       break;
651 
652     case GTK_IMAGE_STOCK:
653     case GTK_IMAGE_ICON_SET:
654     case GTK_IMAGE_EMPTY:
655     default:
656       break;
657     }
658 
659   /* Otherwise we load the surface to guarantee we get a size */
660   if (width == 0)
661     {
662       gtk_icon_helper_ensure_surface (self);
663 
664       if (self->priv->rendered_surface != NULL)
665         {
666           get_surface_size (self, self->priv->rendered_surface, &width, &height);
667         }
668       else if (self->priv->icon_size != GTK_ICON_SIZE_INVALID)
669         {
670           ensure_icon_size (self, &width, &height);
671         }
672     }
673 
674   if (width_out)
675     *width_out = width;
676   if (height_out)
677     *height_out = height;
678 }
679 
680 void
_gtk_icon_helper_set_definition(GtkIconHelper * self,GtkImageDefinition * def)681 _gtk_icon_helper_set_definition (GtkIconHelper *self,
682                                  GtkImageDefinition *def)
683 {
684   if (def)
685     gtk_icon_helper_take_definition (self, gtk_image_definition_ref (def));
686   else
687     _gtk_icon_helper_clear (self);
688 }
689 
690 void
_gtk_icon_helper_set_gicon(GtkIconHelper * self,GIcon * gicon,GtkIconSize icon_size)691 _gtk_icon_helper_set_gicon (GtkIconHelper *self,
692                             GIcon *gicon,
693                             GtkIconSize icon_size)
694 {
695   gtk_icon_helper_take_definition (self, gtk_image_definition_new_gicon (gicon));
696   _gtk_icon_helper_set_icon_size (self, icon_size);
697 }
698 
699 void
_gtk_icon_helper_set_icon_name(GtkIconHelper * self,const gchar * icon_name,GtkIconSize icon_size)700 _gtk_icon_helper_set_icon_name (GtkIconHelper *self,
701                                 const gchar *icon_name,
702                                 GtkIconSize icon_size)
703 {
704   gtk_icon_helper_take_definition (self, gtk_image_definition_new_icon_name (icon_name));
705   _gtk_icon_helper_set_icon_size (self, icon_size);
706 }
707 
708 void
_gtk_icon_helper_set_icon_set(GtkIconHelper * self,GtkIconSet * icon_set,GtkIconSize icon_size)709 _gtk_icon_helper_set_icon_set (GtkIconHelper *self,
710                                GtkIconSet *icon_set,
711                                GtkIconSize icon_size)
712 {
713   gtk_icon_helper_take_definition (self, gtk_image_definition_new_icon_set (icon_set));
714   _gtk_icon_helper_set_icon_size (self, icon_size);
715 }
716 
717 void
_gtk_icon_helper_set_pixbuf(GtkIconHelper * self,GdkPixbuf * pixbuf)718 _gtk_icon_helper_set_pixbuf (GtkIconHelper *self,
719                              GdkPixbuf *pixbuf)
720 {
721   gtk_icon_helper_take_definition (self, gtk_image_definition_new_pixbuf (pixbuf, 1));
722 }
723 
724 void
_gtk_icon_helper_set_animation(GtkIconHelper * self,GdkPixbufAnimation * animation)725 _gtk_icon_helper_set_animation (GtkIconHelper *self,
726                                 GdkPixbufAnimation *animation)
727 {
728   gtk_icon_helper_take_definition (self, gtk_image_definition_new_animation (animation, 1));
729 }
730 
731 void
_gtk_icon_helper_set_surface(GtkIconHelper * self,cairo_surface_t * surface)732 _gtk_icon_helper_set_surface (GtkIconHelper *self,
733 			      cairo_surface_t *surface)
734 {
735   gtk_icon_helper_take_definition (self, gtk_image_definition_new_surface (surface));
736 }
737 
738 void
_gtk_icon_helper_set_stock_id(GtkIconHelper * self,const gchar * stock_id,GtkIconSize icon_size)739 _gtk_icon_helper_set_stock_id (GtkIconHelper *self,
740                                const gchar *stock_id,
741                                GtkIconSize icon_size)
742 {
743   gtk_icon_helper_take_definition (self, gtk_image_definition_new_stock (stock_id));
744   _gtk_icon_helper_set_icon_size (self, icon_size);
745 }
746 
747 gboolean
_gtk_icon_helper_set_icon_size(GtkIconHelper * self,GtkIconSize icon_size)748 _gtk_icon_helper_set_icon_size (GtkIconHelper *self,
749                                 GtkIconSize    icon_size)
750 {
751   if (self->priv->icon_size != icon_size)
752     {
753       self->priv->icon_size = icon_size;
754       gtk_icon_helper_invalidate (self);
755       return TRUE;
756     }
757   return FALSE;
758 }
759 
760 gboolean
_gtk_icon_helper_set_pixel_size(GtkIconHelper * self,gint pixel_size)761 _gtk_icon_helper_set_pixel_size (GtkIconHelper *self,
762                                  gint           pixel_size)
763 {
764   if (self->priv->pixel_size != pixel_size)
765     {
766       self->priv->pixel_size = pixel_size;
767       gtk_icon_helper_invalidate (self);
768       return TRUE;
769     }
770   return FALSE;
771 }
772 
773 gboolean
_gtk_icon_helper_set_use_fallback(GtkIconHelper * self,gboolean use_fallback)774 _gtk_icon_helper_set_use_fallback (GtkIconHelper *self,
775                                    gboolean       use_fallback)
776 {
777   if (self->priv->use_fallback != use_fallback)
778     {
779       self->priv->use_fallback = use_fallback;
780       gtk_icon_helper_invalidate (self);
781       return TRUE;
782     }
783   return FALSE;
784 }
785 
786 GtkImageType
_gtk_icon_helper_get_storage_type(GtkIconHelper * self)787 _gtk_icon_helper_get_storage_type (GtkIconHelper *self)
788 {
789   return gtk_image_definition_get_storage_type (self->priv->def);
790 }
791 
792 gboolean
_gtk_icon_helper_get_use_fallback(GtkIconHelper * self)793 _gtk_icon_helper_get_use_fallback (GtkIconHelper *self)
794 {
795   return self->priv->use_fallback;
796 }
797 
798 GtkIconSize
_gtk_icon_helper_get_icon_size(GtkIconHelper * self)799 _gtk_icon_helper_get_icon_size (GtkIconHelper *self)
800 {
801   return self->priv->icon_size;
802 }
803 
804 gint
_gtk_icon_helper_get_pixel_size(GtkIconHelper * self)805 _gtk_icon_helper_get_pixel_size (GtkIconHelper *self)
806 {
807   return self->priv->pixel_size;
808 }
809 
810 GtkImageDefinition *
gtk_icon_helper_get_definition(GtkIconHelper * self)811 gtk_icon_helper_get_definition (GtkIconHelper *self)
812 {
813   return self->priv->def;
814 }
815 
816 GdkPixbuf *
_gtk_icon_helper_peek_pixbuf(GtkIconHelper * self)817 _gtk_icon_helper_peek_pixbuf (GtkIconHelper *self)
818 {
819   return gtk_image_definition_get_pixbuf (self->priv->def);
820 }
821 
822 GIcon *
_gtk_icon_helper_peek_gicon(GtkIconHelper * self)823 _gtk_icon_helper_peek_gicon (GtkIconHelper *self)
824 {
825   return gtk_image_definition_get_gicon (self->priv->def);
826 }
827 
828 GdkPixbufAnimation *
_gtk_icon_helper_peek_animation(GtkIconHelper * self)829 _gtk_icon_helper_peek_animation (GtkIconHelper *self)
830 {
831   return gtk_image_definition_get_animation (self->priv->def);
832 }
833 
834 GtkIconSet *
_gtk_icon_helper_peek_icon_set(GtkIconHelper * self)835 _gtk_icon_helper_peek_icon_set (GtkIconHelper *self)
836 {
837   return gtk_image_definition_get_icon_set (self->priv->def);
838 }
839 
840 cairo_surface_t *
_gtk_icon_helper_peek_surface(GtkIconHelper * self)841 _gtk_icon_helper_peek_surface (GtkIconHelper *self)
842 {
843   return gtk_image_definition_get_surface (self->priv->def);
844 }
845 
846 const gchar *
_gtk_icon_helper_get_stock_id(GtkIconHelper * self)847 _gtk_icon_helper_get_stock_id (GtkIconHelper *self)
848 {
849   return gtk_image_definition_get_stock (self->priv->def);
850 }
851 
852 const gchar *
_gtk_icon_helper_get_icon_name(GtkIconHelper * self)853 _gtk_icon_helper_get_icon_name (GtkIconHelper *self)
854 {
855   return gtk_image_definition_get_icon_name (self->priv->def);
856 }
857 
858 GtkIconHelper *
gtk_icon_helper_new(GtkCssNode * node,GtkWidget * owner)859 gtk_icon_helper_new (GtkCssNode *node,
860                      GtkWidget  *owner)
861 {
862   g_return_val_if_fail (GTK_IS_CSS_NODE (node), NULL);
863   g_return_val_if_fail (GTK_IS_WIDGET (owner), NULL);
864 
865   return g_object_new (GTK_TYPE_ICON_HELPER,
866                        "node", node,
867                        "owner", owner,
868                        NULL);
869 }
870 
871 GtkCssGadget *
gtk_icon_helper_new_named(const char * name,GtkWidget * owner)872 gtk_icon_helper_new_named (const char *name,
873                            GtkWidget  *owner)
874 {
875   GtkIconHelper *result;
876   GtkCssNode *node;
877 
878   g_return_val_if_fail (name != NULL, NULL);
879   g_return_val_if_fail (GTK_IS_WIDGET (owner), NULL);
880 
881   node = gtk_css_node_new ();
882   gtk_css_node_set_name (node, g_intern_string (name));
883 
884   result = gtk_icon_helper_new (node, owner);
885 
886   g_object_unref (node);
887 
888   return GTK_CSS_GADGET (result);
889 }
890 
891 void
_gtk_icon_helper_draw(GtkIconHelper * self,cairo_t * cr,gdouble x,gdouble y)892 _gtk_icon_helper_draw (GtkIconHelper *self,
893                        cairo_t *cr,
894                        gdouble x,
895                        gdouble y)
896 {
897   GtkCssStyle *style = gtk_css_node_get_style (gtk_css_gadget_get_node (GTK_CSS_GADGET (self)));
898   gtk_icon_helper_ensure_surface (self);
899 
900   if (self->priv->rendered_surface != NULL)
901     {
902       gtk_css_style_render_icon_surface (style,
903                                          cr,
904                                          self->priv->rendered_surface,
905                                          x, y);
906     }
907 }
908 
909 gboolean
_gtk_icon_helper_get_is_empty(GtkIconHelper * self)910 _gtk_icon_helper_get_is_empty (GtkIconHelper *self)
911 {
912   return gtk_image_definition_get_storage_type (self->priv->def) == GTK_IMAGE_EMPTY;
913 }
914 
915 gboolean
_gtk_icon_helper_get_force_scale_pixbuf(GtkIconHelper * self)916 _gtk_icon_helper_get_force_scale_pixbuf (GtkIconHelper *self)
917 {
918   return self->priv->force_scale_pixbuf;
919 }
920 
921 void
_gtk_icon_helper_set_force_scale_pixbuf(GtkIconHelper * self,gboolean force_scale)922 _gtk_icon_helper_set_force_scale_pixbuf (GtkIconHelper *self,
923                                          gboolean       force_scale)
924 {
925   if (self->priv->force_scale_pixbuf != force_scale)
926     {
927       self->priv->force_scale_pixbuf = force_scale;
928       gtk_icon_helper_invalidate (self);
929     }
930 }
931 
932 void
_gtk_icon_helper_set_pixbuf_scale(GtkIconHelper * self,int scale)933 _gtk_icon_helper_set_pixbuf_scale (GtkIconHelper *self,
934 				   int scale)
935 {
936   switch (gtk_image_definition_get_storage_type (self->priv->def))
937   {
938     case GTK_IMAGE_PIXBUF:
939       gtk_icon_helper_take_definition (self,
940                                       gtk_image_definition_new_pixbuf (gtk_image_definition_get_pixbuf (self->priv->def),
941                                                                        scale));
942       break;
943 
944     case GTK_IMAGE_ANIMATION:
945       gtk_icon_helper_take_definition (self,
946                                       gtk_image_definition_new_animation (gtk_image_definition_get_animation (self->priv->def),
947                                                                           scale));
948       break;
949 
950     default:
951       break;
952   }
953 }
954