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 "gtkcsscolorvalueprivate.h"
24#include "gtkcsscolorvalueprivate.h"
25
26#include "gtkstyleproviderprivate.h"
27
28G_DEFINE_TYPE (GtkCssImageFallback, _gtk_css_image_fallback, GTK_TYPE_CSS_IMAGE)
29
30static int
31gtk_css_image_fallback_get_width (GtkCssImage *image)
32{
33 GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
34
35 if (fallback->used < 0)
36 return 0;
37
38 return _gtk_css_image_get_width (image: fallback->images[fallback->used]);
39}
40
41static int
42gtk_css_image_fallback_get_height (GtkCssImage *image)
43{
44 GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
45
46 if (fallback->used < 0)
47 return 0;
48
49 return _gtk_css_image_get_height (image: fallback->images[fallback->used]);
50}
51
52static double
53gtk_css_image_fallback_get_aspect_ratio (GtkCssImage *image)
54{
55 GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
56
57 if (fallback->used < 0)
58 return 0;
59
60 return _gtk_css_image_get_aspect_ratio (image: fallback->images[fallback->used]);
61}
62
63static void
64gtk_css_image_fallback_snapshot (GtkCssImage *image,
65 GtkSnapshot *snapshot,
66 double width,
67 double height)
68{
69 GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
70
71 if (fallback->used < 0)
72 {
73 if (fallback->color)
74 {
75 const GdkRGBA *color = gtk_css_color_value_get_rgba (color: fallback->color);
76 if (!gdk_rgba_is_clear (rgba: color))
77 gtk_snapshot_append_color (snapshot, color,
78 bounds: &GRAPHENE_RECT_INIT (0, 0, width, height));
79 }
80 else
81 {
82 gtk_snapshot_append_color (snapshot, color: &(GdkRGBA) {1, 0, 0, 1},
83 bounds: &GRAPHENE_RECT_INIT (0, 0, width, height));
84 }
85 }
86 else
87 gtk_css_image_snapshot (image: fallback->images[fallback->used], snapshot, width, height);
88}
89
90static void
91gtk_css_image_fallback_print (GtkCssImage *image,
92 GString *string)
93{
94 GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
95 int i;
96
97 g_string_append (string, val: "image(");
98 for (i = 0; i < fallback->n_images; i++)
99 {
100 if (i > 0)
101 g_string_append (string, val: ",");
102 _gtk_css_image_print (image: fallback->images[i], string);
103 }
104 if (fallback->color)
105 {
106 if (fallback->n_images > 0)
107 g_string_append (string, val: ",");
108 _gtk_css_value_print (value: fallback->color, string);
109 }
110
111 g_string_append (string, val: ")");
112}
113
114static void
115gtk_css_image_fallback_dispose (GObject *object)
116{
117 GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (object);
118 int i;
119
120 for (i = 0; i < fallback->n_images; i++)
121 g_object_unref (object: fallback->images[i]);
122 g_free (mem: fallback->images);
123 fallback->images = NULL;
124
125 if (fallback->color)
126 {
127 _gtk_css_value_unref (value: fallback->color);
128 fallback->color = NULL;
129 }
130
131 G_OBJECT_CLASS (_gtk_css_image_fallback_parent_class)->dispose (object);
132}
133
134
135static GtkCssImage *
136gtk_css_image_fallback_compute (GtkCssImage *image,
137 guint property_id,
138 GtkStyleProvider *provider,
139 GtkCssStyle *style,
140 GtkCssStyle *parent_style)
141{
142 GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
143 GtkCssImageFallback *copy;
144 int i;
145
146 if (fallback->used < 0)
147 {
148 GtkCssValue *computed_color = NULL;
149
150 if (fallback->color)
151 computed_color = _gtk_css_value_compute (value: fallback->color,
152 property_id,
153 provider,
154 style,
155 parent_style);
156
157 /* image($color) that didn't change */
158 if (computed_color && !fallback->images &&
159 computed_color == fallback->color)
160 return g_object_ref (image);
161
162 copy = g_object_new (object_type: _gtk_css_image_fallback_get_type (), NULL);
163 copy->n_images = fallback->n_images;
164 copy->images = g_new (GtkCssImage *, fallback->n_images);
165 for (i = 0; i < fallback->n_images; i++)
166 {
167 copy->images[i] = _gtk_css_image_compute (image: fallback->images[i],
168 property_id,
169 provider,
170 style,
171 parent_style);
172
173 if (gtk_css_image_is_invalid (image: copy->images[i]))
174 continue;
175
176 if (copy->used < 0)
177 copy->used = i;
178 }
179
180 copy->color = computed_color;
181
182 return GTK_CSS_IMAGE (copy);
183 }
184 else
185 return GTK_CSS_IMAGE (g_object_ref (fallback));
186}
187
188typedef struct
189{
190 GtkCssValue *color;
191 GPtrArray *images;
192} ParseData;
193
194static guint
195gtk_css_image_fallback_parse_arg (GtkCssParser *parser,
196 guint arg,
197 gpointer _data)
198{
199 ParseData *data = _data;
200
201 if (data->color != NULL)
202 {
203 gtk_css_parser_error_syntax (self: parser, format: "The color must be the last parameter");
204 return 0;
205 }
206 else if (_gtk_css_image_can_parse (parser))
207 {
208 GtkCssImage *image = _gtk_css_image_new_parse (parser);
209 if (image == NULL)
210 return 0;
211
212 if (!data->images)
213 data->images = g_ptr_array_new_with_free_func (element_free_func: g_object_unref);
214
215 g_ptr_array_add (array: data->images, data: image);
216 return 1;
217 }
218 else
219 {
220 data->color = _gtk_css_color_value_parse (parser);
221 if (data->color == NULL)
222 return 0;
223
224 return 1;
225 }
226}
227
228static gboolean
229gtk_css_image_fallback_parse (GtkCssImage *image,
230 GtkCssParser *parser)
231{
232 GtkCssImageFallback *self = GTK_CSS_IMAGE_FALLBACK (image);
233 ParseData data = { NULL, NULL };
234
235 if (!gtk_css_parser_has_function (self: parser, name: "image"))
236 {
237 gtk_css_parser_error_syntax (self: parser, format: "Expected 'image('");
238 return FALSE;
239 }
240
241 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, G_MAXUINT, parse_func: gtk_css_image_fallback_parse_arg, data: &data))
242 {
243 g_clear_pointer (&data.color, _gtk_css_value_unref);
244 if (data.images)
245 g_ptr_array_free (array: data.images, TRUE);
246 return FALSE;
247 }
248
249 self->color = data.color;
250 if (data.images)
251 {
252 self->n_images = data.images->len;
253 self->images = (GtkCssImage **) g_ptr_array_free (array: data.images, FALSE);
254 }
255 else
256 {
257 self->n_images = 0;
258 self->images = NULL;
259 }
260
261 return TRUE;
262}
263
264static gboolean
265gtk_css_image_fallback_equal (GtkCssImage *image1,
266 GtkCssImage *image2)
267{
268 GtkCssImageFallback *fallback1 = GTK_CSS_IMAGE_FALLBACK (image1);
269 GtkCssImageFallback *fallback2 = GTK_CSS_IMAGE_FALLBACK (image2);
270
271 if (fallback1->used < 0)
272 {
273 if (fallback2->used >= 0)
274 return FALSE;
275
276 return _gtk_css_value_equal (value1: fallback1->color, value2: fallback2->color);
277 }
278
279 if (fallback2->used < 0)
280 return FALSE;
281
282 return _gtk_css_image_equal (image1: fallback1->images[fallback1->used],
283 image2: fallback2->images[fallback2->used]);
284}
285
286static gboolean
287gtk_css_image_fallback_is_computed (GtkCssImage *image)
288{
289 GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image);
290
291 if (fallback->used < 0)
292 {
293 guint i;
294
295 if (fallback->color && !fallback->images)
296 return gtk_css_value_is_computed (value: fallback->color);
297
298 for (i = 0; i < fallback->n_images; i++)
299 {
300 if (!gtk_css_image_is_computed (image: fallback->images[i]))
301 {
302 return FALSE;
303 }
304 }
305 }
306
307 return TRUE;
308}
309
310static void
311_gtk_css_image_fallback_class_init (GtkCssImageFallbackClass *klass)
312{
313 GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
314 GObjectClass *object_class = G_OBJECT_CLASS (klass);
315
316 image_class->get_width = gtk_css_image_fallback_get_width;
317 image_class->get_height = gtk_css_image_fallback_get_height;
318 image_class->get_aspect_ratio = gtk_css_image_fallback_get_aspect_ratio;
319 image_class->snapshot = gtk_css_image_fallback_snapshot;
320 image_class->parse = gtk_css_image_fallback_parse;
321 image_class->compute = gtk_css_image_fallback_compute;
322 image_class->print = gtk_css_image_fallback_print;
323 image_class->equal = gtk_css_image_fallback_equal;
324 image_class->is_computed = gtk_css_image_fallback_is_computed;
325
326 object_class->dispose = gtk_css_image_fallback_dispose;
327}
328
329static void
330_gtk_css_image_fallback_init (GtkCssImageFallback *image_fallback)
331{
332 image_fallback->used = -1;
333}
334
335GtkCssImage *
336_gtk_css_image_fallback_new_for_color (GtkCssValue *color)
337{
338 GtkCssImageFallback *image;
339
340 image = g_object_new (GTK_TYPE_CSS_IMAGE_FALLBACK, NULL);
341 image->color = gtk_css_value_ref (value: color);
342
343 return (GtkCssImage *)image;
344}
345

source code of gtk/gtk/gtkcssimagefallback.c