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 | |
28 | G_DEFINE_TYPE (GtkCssImageFallback, _gtk_css_image_fallback, GTK_TYPE_CSS_IMAGE) |
29 | |
30 | static int |
31 | gtk_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 | |
41 | static int |
42 | gtk_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 | |
52 | static double |
53 | gtk_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 | |
63 | static void |
64 | gtk_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 | |
90 | static void |
91 | gtk_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 | |
114 | static void |
115 | gtk_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 | |
135 | static GtkCssImage * |
136 | gtk_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 | |
188 | typedef struct |
189 | { |
190 | GtkCssValue *color; |
191 | GPtrArray *images; |
192 | } ParseData; |
193 | |
194 | static guint |
195 | gtk_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 | |
228 | static gboolean |
229 | gtk_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 | |
264 | static gboolean |
265 | gtk_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 | |
286 | static gboolean |
287 | gtk_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 | |
310 | static 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 | |
329 | static void |
330 | _gtk_css_image_fallback_init (GtkCssImageFallback *image_fallback) |
331 | { |
332 | image_fallback->used = -1; |
333 | } |
334 | |
335 | GtkCssImage * |
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 | |