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
24struct _GtkCssValue {
25 GTK_CSS_VALUE_BASE
26 guint cover :1;
27 guint contain :1;
28 GtkCssValue *x;
29 GtkCssValue *y;
30};
31
32static void
33gtk_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
43static GtkCssValue *
44gtk_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
77static gboolean
78gtk_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
91static GtkCssValue *
92gtk_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
132static void
133gtk_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
155static 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
166static GtkCssValue auto_singleton = { &GTK_CSS_VALUE_BG_SIZE, 1, TRUE, FALSE, FALSE, NULL, NULL };
167static GtkCssValue cover_singleton = { &GTK_CSS_VALUE_BG_SIZE, 1, TRUE, TRUE, FALSE, NULL, NULL };
168static GtkCssValue contain_singleton = { &GTK_CSS_VALUE_BG_SIZE, 1, TRUE, FALSE, TRUE, NULL, NULL };
169
170GtkCssValue *
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, &GTK_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
188GtkCssValue *
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
230static void
231gtk_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
263void
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 == &GTK_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

source code of gtk/gtk/gtkcssbgsizevalue.c