1 /*
2  * Copyright © 2016 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Matthias Clasen <mclasen@redhat.com>
18  */
19 
20 #include "config.h"
21 
22 #include "gtkcssimagefallbackprivate.h"
23 #include "gtkcssimagesurfaceprivate.h"
24 #include "gtkcsscolorvalueprivate.h"
25 #include "gtkcssrgbavalueprivate.h"
26 
27 #include "gtkstyleproviderprivate.h"
28 
G_DEFINE_TYPE(GtkCssImageFallback,_gtk_css_image_fallback,GTK_TYPE_CSS_IMAGE)29 G_DEFINE_TYPE (GtkCssImageFallback, _gtk_css_image_fallback, GTK_TYPE_CSS_IMAGE)
30 
31 static int
32 gtk_css_image_fallback_get_width (GtkCssImage *image)
33 {
34   GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
35 
36   if (fallback->used < 0)
37     return 0;
38 
39   return _gtk_css_image_get_width (fallback->images[fallback->used]);
40 }
41 
42 static int
gtk_css_image_fallback_get_height(GtkCssImage * image)43 gtk_css_image_fallback_get_height (GtkCssImage *image)
44 {
45   GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
46 
47   if (fallback->used < 0)
48     return 0;
49 
50   return _gtk_css_image_get_height (fallback->images[fallback->used]);
51 }
52 
53 static double
gtk_css_image_fallback_get_aspect_ratio(GtkCssImage * image)54 gtk_css_image_fallback_get_aspect_ratio (GtkCssImage *image)
55 {
56   GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
57 
58   if (fallback->used < 0)
59     return 0;
60 
61   return _gtk_css_image_get_aspect_ratio (fallback->images[fallback->used]);
62 }
63 
64 static void
gtk_css_image_fallback_draw(GtkCssImage * image,cairo_t * cr,double width,double height)65 gtk_css_image_fallback_draw (GtkCssImage *image,
66                              cairo_t     *cr,
67                              double       width,
68                              double       height)
69 {
70   GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
71 
72   if (fallback->used < 0)
73     {
74       if (fallback->color)
75         gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (fallback->color));
76       else
77         cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
78 
79       cairo_rectangle (cr, 0, 0, width, height);
80       cairo_fill (cr);
81     }
82   else
83     _gtk_css_image_draw (fallback->images[fallback->used], cr, width, height);
84 }
85 
86 static void
gtk_css_image_fallback_print(GtkCssImage * image,GString * string)87 gtk_css_image_fallback_print (GtkCssImage *image,
88                               GString     *string)
89 {
90   GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
91   int i;
92 
93   g_string_append (string, "image(");
94   for (i = 0; i < fallback->n_images; i++)
95     {
96       if (i > 0)
97         g_string_append (string, ",");
98       _gtk_css_image_print (fallback->images[i], string);
99     }
100   if (fallback->color)
101     {
102       if (fallback->n_images > 0)
103         g_string_append (string, ",");
104       _gtk_css_value_print (fallback->color, string);
105     }
106 
107   g_string_append (string, ")");
108 }
109 
110 static void
gtk_css_image_fallback_dispose(GObject * object)111 gtk_css_image_fallback_dispose (GObject *object)
112 {
113   GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (object);
114   int i;
115 
116   for (i = 0; i < fallback->n_images; i++)
117     g_object_unref (fallback->images[i]);
118   g_free (fallback->images);
119   fallback->images = NULL;
120 
121   if (fallback->color)
122     {
123       _gtk_css_value_unref (fallback->color);
124       fallback->color = NULL;
125     }
126 
127   G_OBJECT_CLASS (_gtk_css_image_fallback_parent_class)->dispose (object);
128 }
129 
130 
131 static GtkCssImage *
gtk_css_image_fallback_compute(GtkCssImage * image,guint property_id,GtkStyleProviderPrivate * provider,GtkCssStyle * style,GtkCssStyle * parent_style)132 gtk_css_image_fallback_compute (GtkCssImage             *image,
133                                 guint                    property_id,
134                                 GtkStyleProviderPrivate *provider,
135                                 GtkCssStyle             *style,
136                                 GtkCssStyle             *parent_style)
137 {
138   GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
139   GtkCssImageFallback *copy;
140   int i;
141 
142   if (fallback->used < 0)
143     {
144       copy = g_object_new (_gtk_css_image_fallback_get_type (), NULL);
145       copy->n_images = fallback->n_images;
146       copy->images = g_new (GtkCssImage *, fallback->n_images);
147       for (i = 0; i < fallback->n_images; i++)
148         {
149           copy->images[i] = _gtk_css_image_compute (fallback->images[i],
150                                                     property_id,
151                                                     provider,
152                                                     style,
153                                                     parent_style);
154 
155           /* Assume that failing to load an image leaves a 0x0 surface image */
156           if (GTK_IS_CSS_IMAGE_SURFACE (copy->images[i]) &&
157               _gtk_css_image_get_width (copy->images[i]) == 0 &&
158               _gtk_css_image_get_height (copy->images[i]) == 0)
159             continue;
160 
161           if (copy->used < 0)
162             copy->used = i;
163         }
164 
165       if (fallback->color)
166         copy->color = _gtk_css_value_compute (fallback->color,
167                                               property_id,
168                                               provider,
169                                               style,
170                                               parent_style);
171       else
172         copy->color = NULL;
173 
174       return GTK_CSS_IMAGE (copy);
175     }
176   else
177     return GTK_CSS_IMAGE (g_object_ref (fallback));
178 }
179 
180 static gboolean
gtk_css_image_fallback_parse(GtkCssImage * image,GtkCssParser * parser)181 gtk_css_image_fallback_parse (GtkCssImage  *image,
182                               GtkCssParser *parser)
183 {
184   GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
185   GPtrArray *images;
186   GtkCssImage *child;
187 
188   if (!_gtk_css_parser_try (parser, "image", TRUE))
189     {
190       _gtk_css_parser_error (parser, "'image'");
191       return FALSE;
192     }
193 
194   if (!_gtk_css_parser_try (parser, "(", TRUE))
195     {
196       _gtk_css_parser_error (parser,
197                              "Expected '(' after 'image'");
198       return FALSE;
199     }
200 
201   images = g_ptr_array_new_with_free_func (g_object_unref);
202 
203   do
204     {
205       child = NULL;
206       if (_gtk_css_image_can_parse (parser))
207         child = _gtk_css_image_new_parse (parser);
208       if (child == NULL)
209         {
210           fallback->color = _gtk_css_color_value_parse (parser);
211           if (fallback->color)
212             break;
213 
214           g_ptr_array_free (images, TRUE);
215           return FALSE;
216         }
217       g_ptr_array_add (images, child);
218     }
219   while ( _gtk_css_parser_try (parser, ",", TRUE));
220 
221   if (!_gtk_css_parser_try (parser, ")", TRUE))
222     {
223       g_ptr_array_free (images, TRUE);
224       _gtk_css_parser_error (parser,
225                              "Expected ')' at end of 'image'");
226       return FALSE;
227     }
228 
229   fallback->n_images = images->len;
230   fallback->images = (GtkCssImage **) g_ptr_array_free (images, FALSE);
231 
232   return TRUE;
233 }
234 
235 static void
_gtk_css_image_fallback_class_init(GtkCssImageFallbackClass * klass)236 _gtk_css_image_fallback_class_init (GtkCssImageFallbackClass *klass)
237 {
238   GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
239   GObjectClass *object_class = G_OBJECT_CLASS (klass);
240 
241   image_class->get_width = gtk_css_image_fallback_get_width;
242   image_class->get_height = gtk_css_image_fallback_get_height;
243   image_class->get_aspect_ratio = gtk_css_image_fallback_get_aspect_ratio;
244   image_class->draw = gtk_css_image_fallback_draw;
245   image_class->parse = gtk_css_image_fallback_parse;
246   image_class->compute = gtk_css_image_fallback_compute;
247   image_class->print = gtk_css_image_fallback_print;
248 
249   object_class->dispose = gtk_css_image_fallback_dispose;
250 }
251 
252 static void
_gtk_css_image_fallback_init(GtkCssImageFallback * image_fallback)253 _gtk_css_image_fallback_init (GtkCssImageFallback *image_fallback)
254 {
255   image_fallback->used = -1;
256 }
257