1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 2011 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 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 | |
18 | #include "config.h" |
19 | |
20 | #include "gtkcssbgsizevalueprivate.h" |
21 | |
22 | #include "gtkcssnumbervalueprivate.h" |
23 | |
24 | struct _GtkCssValue { |
25 | GTK_CSS_VALUE_BASE |
26 | guint cover :1; |
27 | guint contain :1; |
28 | GtkCssValue *x; |
29 | GtkCssValue *y; |
30 | }; |
31 | |
32 | static void |
33 | gtk_css_value_bg_size_free (GtkCssValue *value) |
34 | { |
35 | if (value->x) |
36 | _gtk_css_value_unref (value: value->x); |
37 | if (value->y) |
38 | _gtk_css_value_unref (value: value->y); |
39 | |
40 | g_slice_free (GtkCssValue, value); |
41 | } |
42 | |
43 | static GtkCssValue * |
44 | gtk_css_value_bg_size_compute (GtkCssValue *value, |
45 | guint property_id, |
46 | GtkStyleProvider *provider, |
47 | GtkCssStyle *style, |
48 | GtkCssStyle *parent_style) |
49 | { |
50 | GtkCssValue *x, *y; |
51 | |
52 | if (value->x == NULL && value->y == NULL) |
53 | return _gtk_css_value_ref (value); |
54 | |
55 | x = y = NULL; |
56 | |
57 | if (value->x) |
58 | x = _gtk_css_value_compute (value: value->x, property_id, provider, style, parent_style); |
59 | |
60 | if (value->y) |
61 | y = _gtk_css_value_compute (value: value->y, property_id, provider, style, parent_style); |
62 | |
63 | if (x == value->x && y == value->y) |
64 | { |
65 | if (x) |
66 | _gtk_css_value_unref (value: x); |
67 | if (y) |
68 | _gtk_css_value_unref (value: y); |
69 | |
70 | return _gtk_css_value_ref (value); |
71 | } |
72 | |
73 | return _gtk_css_bg_size_value_new (x: value->x ? x : NULL, |
74 | y: value->y ? y : NULL); |
75 | } |
76 | |
77 | static gboolean |
78 | gtk_css_value_bg_size_equal (const GtkCssValue *value1, |
79 | const GtkCssValue *value2) |
80 | { |
81 | return value1->cover == value2->cover && |
82 | value1->contain == value2->contain && |
83 | (value1->x == value2->x || |
84 | (value1->x != NULL && value2->x != NULL && |
85 | _gtk_css_value_equal (value1: value1->x, value2: value2->x))) && |
86 | (value1->y == value2->y || |
87 | (value1->y != NULL && value2->y != NULL && |
88 | _gtk_css_value_equal (value1: value1->y, value2: value2->y))); |
89 | } |
90 | |
91 | static GtkCssValue * |
92 | gtk_css_value_bg_size_transition (GtkCssValue *start, |
93 | GtkCssValue *end, |
94 | guint property_id, |
95 | double progress) |
96 | { |
97 | GtkCssValue *x, *y; |
98 | |
99 | if (start->cover) |
100 | return end->cover ? _gtk_css_value_ref (value: end) : NULL; |
101 | if (start->contain) |
102 | return end->contain ? _gtk_css_value_ref (value: end) : NULL; |
103 | |
104 | if ((start->x != NULL) ^ (end->x != NULL) || |
105 | (start->y != NULL) ^ (end->y != NULL)) |
106 | return NULL; |
107 | |
108 | if (start->x) |
109 | { |
110 | x = _gtk_css_value_transition (start: start->x, end: end->x, property_id, progress); |
111 | if (x == NULL) |
112 | return NULL; |
113 | } |
114 | else |
115 | x = NULL; |
116 | |
117 | if (start->y) |
118 | { |
119 | y = _gtk_css_value_transition (start: start->y, end: end->y, property_id, progress); |
120 | if (y == NULL) |
121 | { |
122 | _gtk_css_value_unref (value: x); |
123 | return NULL; |
124 | } |
125 | } |
126 | else |
127 | y = NULL; |
128 | |
129 | return _gtk_css_bg_size_value_new (x, y); |
130 | } |
131 | |
132 | static void |
133 | gtk_css_value_bg_size_print (const GtkCssValue *value, |
134 | GString *string) |
135 | { |
136 | if (value->cover) |
137 | g_string_append (string, val: "cover" ); |
138 | else if (value->contain) |
139 | g_string_append (string, val: "contain" ); |
140 | else |
141 | { |
142 | if (value->x == NULL) |
143 | g_string_append (string, val: "auto" ); |
144 | else |
145 | _gtk_css_value_print (value: value->x, string); |
146 | |
147 | if (value->y) |
148 | { |
149 | g_string_append_c (string, ' '); |
150 | _gtk_css_value_print (value: value->y, string); |
151 | } |
152 | } |
153 | } |
154 | |
155 | static const GtkCssValueClass GTK_CSS_VALUE_BG_SIZE = { |
156 | "GtkCssBgSizeValue" , |
157 | gtk_css_value_bg_size_free, |
158 | gtk_css_value_bg_size_compute, |
159 | gtk_css_value_bg_size_equal, |
160 | gtk_css_value_bg_size_transition, |
161 | NULL, |
162 | NULL, |
163 | gtk_css_value_bg_size_print |
164 | }; |
165 | |
166 | static GtkCssValue auto_singleton = { >K_CSS_VALUE_BG_SIZE, 1, TRUE, FALSE, FALSE, NULL, NULL }; |
167 | static GtkCssValue cover_singleton = { >K_CSS_VALUE_BG_SIZE, 1, TRUE, TRUE, FALSE, NULL, NULL }; |
168 | static GtkCssValue contain_singleton = { >K_CSS_VALUE_BG_SIZE, 1, TRUE, FALSE, TRUE, NULL, NULL }; |
169 | |
170 | GtkCssValue * |
171 | _gtk_css_bg_size_value_new (GtkCssValue *x, |
172 | GtkCssValue *y) |
173 | { |
174 | GtkCssValue *result; |
175 | |
176 | if (x == NULL && y == NULL) |
177 | return _gtk_css_value_ref (value: &auto_singleton); |
178 | |
179 | result = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_BG_SIZE); |
180 | result->x = x; |
181 | result->y = y; |
182 | result->is_computed = (!x || gtk_css_value_is_computed (value: x)) && |
183 | (!y || gtk_css_value_is_computed (value: y)); |
184 | |
185 | return result; |
186 | } |
187 | |
188 | GtkCssValue * |
189 | _gtk_css_bg_size_value_parse (GtkCssParser *parser) |
190 | { |
191 | GtkCssValue *x, *y; |
192 | |
193 | if (gtk_css_parser_try_ident (self: parser, ident: "cover" )) |
194 | return _gtk_css_value_ref (value: &cover_singleton); |
195 | else if (gtk_css_parser_try_ident (self: parser, ident: "contain" )) |
196 | return _gtk_css_value_ref (value: &contain_singleton); |
197 | |
198 | if (gtk_css_parser_try_ident (self: parser, ident: "auto" )) |
199 | x = NULL; |
200 | else |
201 | { |
202 | x = _gtk_css_number_value_parse (parser, |
203 | flags: GTK_CSS_POSITIVE_ONLY |
204 | | GTK_CSS_PARSE_PERCENT |
205 | | GTK_CSS_PARSE_LENGTH); |
206 | if (x == NULL) |
207 | return NULL; |
208 | } |
209 | |
210 | if (gtk_css_parser_try_ident (self: parser, ident: "auto" )) |
211 | y = NULL; |
212 | else if (!gtk_css_number_value_can_parse (parser)) |
213 | y = NULL; |
214 | else |
215 | { |
216 | y = _gtk_css_number_value_parse (parser, |
217 | flags: GTK_CSS_POSITIVE_ONLY |
218 | | GTK_CSS_PARSE_PERCENT |
219 | | GTK_CSS_PARSE_LENGTH); |
220 | if (y == NULL) |
221 | { |
222 | _gtk_css_value_unref (value: x); |
223 | return NULL; |
224 | } |
225 | } |
226 | |
227 | return _gtk_css_bg_size_value_new (x, y); |
228 | } |
229 | |
230 | static void |
231 | gtk_css_bg_size_compute_size_for_cover_contain (gboolean cover, |
232 | GtkCssImage *image, |
233 | double width, |
234 | double height, |
235 | double *concrete_width, |
236 | double *concrete_height) |
237 | { |
238 | double aspect, image_aspect; |
239 | |
240 | image_aspect = _gtk_css_image_get_aspect_ratio (image); |
241 | if (image_aspect == 0.0) |
242 | { |
243 | *concrete_width = width; |
244 | *concrete_height = height; |
245 | return; |
246 | } |
247 | |
248 | aspect = width / height; |
249 | |
250 | if ((aspect >= image_aspect && cover) || |
251 | (aspect < image_aspect && !cover)) |
252 | { |
253 | *concrete_width = width; |
254 | *concrete_height = width / image_aspect; |
255 | } |
256 | else |
257 | { |
258 | *concrete_height = height; |
259 | *concrete_width = height * image_aspect; |
260 | } |
261 | } |
262 | |
263 | void |
264 | _gtk_css_bg_size_value_compute_size (const GtkCssValue *value, |
265 | GtkCssImage *image, |
266 | double area_width, |
267 | double area_height, |
268 | double *out_width, |
269 | double *out_height) |
270 | { |
271 | g_return_if_fail (value->class == >K_CSS_VALUE_BG_SIZE); |
272 | |
273 | if (value->contain || value->cover) |
274 | { |
275 | gtk_css_bg_size_compute_size_for_cover_contain (cover: value->cover, |
276 | image, |
277 | width: area_width, height: area_height, |
278 | concrete_width: out_width, concrete_height: out_height); |
279 | } |
280 | else |
281 | { |
282 | double x, y; |
283 | |
284 | /* note: 0 does the right thing later for 'auto' */ |
285 | x = value->x ? _gtk_css_number_value_get (number: value->x, one_hundred_percent: area_width) : 0; |
286 | y = value->y ? _gtk_css_number_value_get (number: value->y, one_hundred_percent: area_height) : 0; |
287 | |
288 | if ((x <= 0 && value->x) || |
289 | (y <= 0 && value->y)) |
290 | { |
291 | *out_width = 0; |
292 | *out_height = 0; |
293 | } |
294 | else |
295 | { |
296 | _gtk_css_image_get_concrete_size (image, |
297 | specified_width: x, specified_height: y, |
298 | default_width: area_width, default_height: area_height, |
299 | concrete_width: out_width, concrete_height: out_height); |
300 | } |
301 | } |
302 | } |
303 | |
304 | |