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