1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 2016 Benjamin Otte <otte@gnome.org> |
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 "gtkcssnumbervalueprivate.h" |
21 | |
22 | #include "gtkcsscalcvalueprivate.h" |
23 | #include "gtkcssenumvalueprivate.h" |
24 | #include "gtkcssdimensionvalueprivate.h" |
25 | #include "gtkcssstyleprivate.h" |
26 | #include "gtkprivate.h" |
27 | |
28 | static GtkCssValue * gtk_css_calc_value_new (guint n_terms); |
29 | static GtkCssValue * gtk_css_calc_value_new_sum (GtkCssValue *a, |
30 | GtkCssValue *b); |
31 | static gsize gtk_css_value_calc_get_size (gsize n_terms); |
32 | |
33 | enum { |
34 | TYPE_CALC = 0, |
35 | TYPE_DIMENSION = 1, |
36 | }; |
37 | |
38 | struct _GtkCssValue { |
39 | GTK_CSS_VALUE_BASE |
40 | guint type : 1; /* Calc or dimension */ |
41 | union { |
42 | struct { |
43 | GtkCssUnit unit; |
44 | double value; |
45 | } dimension; |
46 | struct { |
47 | guint n_terms; |
48 | GtkCssValue *terms[1]; |
49 | } calc; |
50 | }; |
51 | }; |
52 | |
53 | |
54 | static GtkCssValue * |
55 | gtk_css_calc_value_new_from_array (GtkCssValue **values, |
56 | guint n_values) |
57 | { |
58 | GtkCssValue *result; |
59 | |
60 | if (n_values > 1) |
61 | { |
62 | result = gtk_css_calc_value_new (n_terms: n_values); |
63 | memcpy (dest: result->calc.terms, src: values, n: n_values * sizeof (GtkCssValue *)); |
64 | } |
65 | else |
66 | { |
67 | result = values[0]; |
68 | } |
69 | |
70 | return result; |
71 | } |
72 | |
73 | static void |
74 | gtk_css_value_number_free (GtkCssValue *number) |
75 | { |
76 | if (number->type == TYPE_CALC) |
77 | { |
78 | const guint n_terms = number->calc.n_terms; |
79 | |
80 | for (guint i = 0; i < n_terms; i++) |
81 | _gtk_css_value_unref (value: number->calc.terms[i]); |
82 | |
83 | g_slice_free1 (block_size: gtk_css_value_calc_get_size (n_terms), mem_block: number); |
84 | } |
85 | else |
86 | { |
87 | g_slice_free (GtkCssValue, number); |
88 | } |
89 | } |
90 | |
91 | static double |
92 | get_dpi (GtkCssStyle *style) |
93 | { |
94 | return _gtk_css_number_value_get (number: style->core->dpi, one_hundred_percent: 96); |
95 | } |
96 | |
97 | static double |
98 | get_base_font_size_px (guint property_id, |
99 | GtkStyleProvider *provider, |
100 | GtkCssStyle *style, |
101 | GtkCssStyle *parent_style) |
102 | { |
103 | if (property_id == GTK_CSS_PROPERTY_FONT_SIZE) |
104 | { |
105 | if (parent_style) |
106 | return _gtk_css_number_value_get (number: parent_style->core->font_size, one_hundred_percent: 100); |
107 | else |
108 | return gtk_css_font_size_get_default_px (provider, style); |
109 | } |
110 | |
111 | return _gtk_css_number_value_get (number: style->core->font_size, one_hundred_percent: 100); |
112 | } |
113 | |
114 | static GtkCssValue * |
115 | gtk_css_value_number_compute (GtkCssValue *number, |
116 | guint property_id, |
117 | GtkStyleProvider *provider, |
118 | GtkCssStyle *style, |
119 | GtkCssStyle *parent_style) |
120 | { |
121 | double value; |
122 | |
123 | if (G_UNLIKELY (number->type == TYPE_CALC)) |
124 | { |
125 | const guint n_terms = number->calc.n_terms; |
126 | GtkCssValue *result; |
127 | gboolean changed = FALSE; |
128 | gsize i; |
129 | GtkCssValue **new_values; |
130 | |
131 | new_values = g_alloca (sizeof (GtkCssValue *) * n_terms); |
132 | |
133 | for (i = 0; i < n_terms; i++) |
134 | { |
135 | GtkCssValue *computed = _gtk_css_value_compute (value: number->calc.terms[i], |
136 | property_id, provider, style, |
137 | parent_style); |
138 | changed |= computed != number->calc.terms[i]; |
139 | new_values[i] = computed; |
140 | } |
141 | |
142 | if (changed) |
143 | { |
144 | result = gtk_css_calc_value_new_from_array (values: new_values, n_values: n_terms); |
145 | } |
146 | else |
147 | { |
148 | for (i = 0; i < n_terms; i++) |
149 | gtk_css_value_unref (value: new_values[i]); |
150 | |
151 | result = _gtk_css_value_ref (value: number); |
152 | } |
153 | |
154 | return result; |
155 | } |
156 | |
157 | g_assert (number->type == TYPE_DIMENSION); |
158 | |
159 | value = number->dimension.value; |
160 | switch (number->dimension.unit) |
161 | { |
162 | case GTK_CSS_PERCENT: |
163 | /* percentages for font sizes are computed, other percentages aren't */ |
164 | if (property_id == GTK_CSS_PROPERTY_FONT_SIZE) |
165 | return gtk_css_dimension_value_new (value: value / 100.0 * |
166 | get_base_font_size_px (property_id, provider, style, parent_style), |
167 | unit: GTK_CSS_PX); |
168 | G_GNUC_FALLTHROUGH; |
169 | case GTK_CSS_NUMBER: |
170 | case GTK_CSS_PX: |
171 | case GTK_CSS_DEG: |
172 | case GTK_CSS_S: |
173 | return _gtk_css_value_ref (value: number); |
174 | case GTK_CSS_PT: |
175 | return gtk_css_dimension_value_new (value: value * get_dpi (style) / 72.0, |
176 | unit: GTK_CSS_PX); |
177 | case GTK_CSS_PC: |
178 | return gtk_css_dimension_value_new (value: value * get_dpi (style) / 72.0 * 12.0, |
179 | unit: GTK_CSS_PX); |
180 | case GTK_CSS_IN: |
181 | return gtk_css_dimension_value_new (value: value * get_dpi (style), |
182 | unit: GTK_CSS_PX); |
183 | case GTK_CSS_CM: |
184 | return gtk_css_dimension_value_new (value: value * get_dpi (style) * 0.39370078740157477, |
185 | unit: GTK_CSS_PX); |
186 | case GTK_CSS_MM: |
187 | return gtk_css_dimension_value_new (value: value * get_dpi (style) * 0.039370078740157477, |
188 | unit: GTK_CSS_PX); |
189 | case GTK_CSS_EM: |
190 | return gtk_css_dimension_value_new (value: value * |
191 | get_base_font_size_px (property_id, provider, style, parent_style), |
192 | unit: GTK_CSS_PX); |
193 | case GTK_CSS_EX: |
194 | /* for now we pretend ex is half of em */ |
195 | return gtk_css_dimension_value_new (value: value * 0.5 * |
196 | get_base_font_size_px (property_id, provider, style, parent_style), |
197 | unit: GTK_CSS_PX); |
198 | case GTK_CSS_REM: |
199 | return gtk_css_dimension_value_new (value: value * |
200 | gtk_css_font_size_get_default_px (provider, style), |
201 | unit: GTK_CSS_PX); |
202 | case GTK_CSS_RAD: |
203 | return gtk_css_dimension_value_new (value: value * 360.0 / (2 * G_PI), |
204 | unit: GTK_CSS_DEG); |
205 | case GTK_CSS_GRAD: |
206 | return gtk_css_dimension_value_new (value: value * 360.0 / 400.0, |
207 | unit: GTK_CSS_DEG); |
208 | case GTK_CSS_TURN: |
209 | return gtk_css_dimension_value_new (value: value * 360.0, |
210 | unit: GTK_CSS_DEG); |
211 | case GTK_CSS_MS: |
212 | return gtk_css_dimension_value_new (value: value / 1000.0, |
213 | unit: GTK_CSS_S); |
214 | default: |
215 | g_assert_not_reached(); |
216 | } |
217 | } |
218 | |
219 | static gboolean |
220 | gtk_css_value_number_equal (const GtkCssValue *val1, |
221 | const GtkCssValue *val2) |
222 | { |
223 | guint i; |
224 | |
225 | if (val1->type != val2->type) |
226 | return FALSE; |
227 | |
228 | if (G_LIKELY (val1->type == TYPE_DIMENSION)) |
229 | { |
230 | return val1->dimension.unit == val2->dimension.unit && |
231 | val1->dimension.value == val2->dimension.value; |
232 | } |
233 | |
234 | g_assert (val1->type == TYPE_CALC); |
235 | |
236 | if (val1->calc.n_terms != val2->calc.n_terms) |
237 | return FALSE; |
238 | |
239 | for (i = 0; i < val1->calc.n_terms; i++) |
240 | { |
241 | if (!_gtk_css_value_equal (value1: val1->calc.terms[i], value2: val2->calc.terms[i])) |
242 | return FALSE; |
243 | } |
244 | |
245 | return TRUE; |
246 | } |
247 | |
248 | static void |
249 | gtk_css_value_number_print (const GtkCssValue *value, |
250 | GString *string) |
251 | { |
252 | guint i; |
253 | |
254 | if (G_LIKELY (value->type == TYPE_DIMENSION)) |
255 | { |
256 | const char *names[] = { |
257 | /* [GTK_CSS_NUMBER] = */ "" , |
258 | /* [GTK_CSS_PERCENT] = */ "%" , |
259 | /* [GTK_CSS_PX] = */ "px" , |
260 | /* [GTK_CSS_PT] = */ "pt" , |
261 | /* [GTK_CSS_EM] = */ "em" , |
262 | /* [GTK_CSS_EX] = */ "ex" , |
263 | /* [GTK_CSS_REM] = */ "rem" , |
264 | /* [GTK_CSS_PC] = */ "pc" , |
265 | /* [GTK_CSS_IN] = */ "in" , |
266 | /* [GTK_CSS_CM] = */ "cm" , |
267 | /* [GTK_CSS_MM] = */ "mm" , |
268 | /* [GTK_CSS_RAD] = */ "rad" , |
269 | /* [GTK_CSS_DEG] = */ "deg" , |
270 | /* [GTK_CSS_GRAD] = */ "grad" , |
271 | /* [GTK_CSS_TURN] = */ "turn" , |
272 | /* [GTK_CSS_S] = */ "s" , |
273 | /* [GTK_CSS_MS] = */ "ms" , |
274 | }; |
275 | char buf[G_ASCII_DTOSTR_BUF_SIZE]; |
276 | |
277 | if (isinf (value->dimension.value)) |
278 | g_string_append (string, val: "infinite" ); |
279 | else |
280 | { |
281 | g_ascii_dtostr (buffer: buf, buf_len: sizeof (buf), d: value->dimension.value); |
282 | g_string_append (string, val: buf); |
283 | if (value->dimension.value != 0.0) |
284 | g_string_append (string, val: names[value->dimension.unit]); |
285 | } |
286 | |
287 | return; |
288 | } |
289 | |
290 | g_assert (value->type == TYPE_CALC); |
291 | |
292 | g_string_append (string, val: "calc(" ); |
293 | _gtk_css_value_print (value: value->calc.terms[0], string); |
294 | |
295 | for (i = 1; i < value->calc.n_terms; i++) |
296 | { |
297 | g_string_append (string, val: " + " ); |
298 | _gtk_css_value_print (value: value->calc.terms[i], string); |
299 | } |
300 | g_string_append (string, val: ")" ); |
301 | } |
302 | |
303 | static GtkCssValue * |
304 | gtk_css_value_number_transition (GtkCssValue *start, |
305 | GtkCssValue *end, |
306 | guint property_id, |
307 | double progress) |
308 | { |
309 | GtkCssValue *result, *mul_start, *mul_end; |
310 | |
311 | if (start == end) |
312 | return _gtk_css_value_ref (value: start); |
313 | |
314 | if (G_LIKELY (start->type == TYPE_DIMENSION && end->type == TYPE_DIMENSION)) |
315 | { |
316 | if (start->dimension.unit == end->dimension.unit) |
317 | { |
318 | const double start_value = start->dimension.value; |
319 | const double end_value = end->dimension.value; |
320 | return gtk_css_dimension_value_new (value: start_value + (end_value - start_value) * progress, |
321 | unit: start->dimension.unit); |
322 | } |
323 | } |
324 | |
325 | mul_start = gtk_css_number_value_multiply (value: start, factor: 1 - progress); |
326 | mul_end = gtk_css_number_value_multiply (value: end, factor: progress); |
327 | |
328 | result = gtk_css_number_value_add (value1: mul_start, value2: mul_end); |
329 | |
330 | _gtk_css_value_unref (value: mul_start); |
331 | _gtk_css_value_unref (value: mul_end); |
332 | |
333 | return result; |
334 | } |
335 | |
336 | static const GtkCssValueClass GTK_CSS_VALUE_NUMBER = { |
337 | "GtkCssNumberValue" , |
338 | gtk_css_value_number_free, |
339 | gtk_css_value_number_compute, |
340 | gtk_css_value_number_equal, |
341 | gtk_css_value_number_transition, |
342 | NULL, |
343 | NULL, |
344 | gtk_css_value_number_print |
345 | }; |
346 | |
347 | static gsize |
348 | gtk_css_value_calc_get_size (gsize n_terms) |
349 | { |
350 | g_assert (n_terms > 0); |
351 | |
352 | return sizeof (GtkCssValue) + sizeof (GtkCssValue *) * (n_terms - 1); |
353 | } |
354 | |
355 | static GtkCssValue * |
356 | gtk_css_calc_value_new (guint n_terms) |
357 | { |
358 | GtkCssValue *result; |
359 | |
360 | result = _gtk_css_value_alloc (klass: >K_CSS_VALUE_NUMBER, |
361 | size: gtk_css_value_calc_get_size (n_terms)); |
362 | result->type = TYPE_CALC; |
363 | result->calc.n_terms = n_terms; |
364 | |
365 | return result; |
366 | } |
367 | |
368 | GtkCssValue * |
369 | gtk_css_dimension_value_new (double value, |
370 | GtkCssUnit unit) |
371 | { |
372 | static GtkCssValue number_singletons[] = { |
373 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_NUMBER, 0 }} }, |
374 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_NUMBER, 1 }} }, |
375 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_NUMBER, 96 }} }, /* DPI default */ |
376 | }; |
377 | static GtkCssValue px_singletons[] = { |
378 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 0 }} }, |
379 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 1 }} }, |
380 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 2 }} }, |
381 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 3 }} }, |
382 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 4 }} }, |
383 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 8 }} }, |
384 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 16 }} }, /* Icon size default */ |
385 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 32 }} }, |
386 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 64 }} }, |
387 | }; |
388 | static GtkCssValue percent_singletons[] = { |
389 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PERCENT, 0 }} }, |
390 | { >K_CSS_VALUE_NUMBER, 1, FALSE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PERCENT, 50 }} }, |
391 | { >K_CSS_VALUE_NUMBER, 1, FALSE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PERCENT, 100 }} }, |
392 | }; |
393 | static GtkCssValue second_singletons[] = { |
394 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_S, 0 }} }, |
395 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_S, 1 }} }, |
396 | }; |
397 | static GtkCssValue deg_singletons[] = { |
398 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_DEG, 0 }} }, |
399 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_DEG, 90 }} }, |
400 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_DEG, 180 }} }, |
401 | { >K_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_DEG, 270 }} }, |
402 | }; |
403 | GtkCssValue *result; |
404 | |
405 | switch ((guint)unit) |
406 | { |
407 | case GTK_CSS_NUMBER: |
408 | if (value == 0 || value == 1) |
409 | return _gtk_css_value_ref (value: &number_singletons[(int) value]); |
410 | |
411 | if (value == 96) |
412 | return _gtk_css_value_ref (value: &number_singletons[2]); |
413 | |
414 | break; |
415 | |
416 | case GTK_CSS_PX: |
417 | if (value == 0 || |
418 | value == 1 || |
419 | value == 2 || |
420 | value == 3 || |
421 | value == 4) |
422 | return _gtk_css_value_ref (value: &px_singletons[(int) value]); |
423 | if (value == 8) |
424 | return _gtk_css_value_ref (value: &px_singletons[5]); |
425 | if (value == 16) |
426 | return _gtk_css_value_ref (value: &px_singletons[6]); |
427 | if (value == 32) |
428 | return _gtk_css_value_ref (value: &px_singletons[7]); |
429 | if (value == 64) |
430 | return _gtk_css_value_ref (value: &px_singletons[8]); |
431 | |
432 | break; |
433 | |
434 | case GTK_CSS_PERCENT: |
435 | if (value == 0) |
436 | return _gtk_css_value_ref (value: &percent_singletons[0]); |
437 | if (value == 50) |
438 | return _gtk_css_value_ref (value: &percent_singletons[1]); |
439 | if (value == 100) |
440 | return _gtk_css_value_ref (value: &percent_singletons[2]); |
441 | |
442 | break; |
443 | |
444 | case GTK_CSS_S: |
445 | if (value == 0 || value == 1) |
446 | return _gtk_css_value_ref (value: &second_singletons[(int)value]); |
447 | |
448 | break; |
449 | |
450 | case GTK_CSS_DEG: |
451 | if (value == 0) |
452 | return _gtk_css_value_ref (value: °_singletons[0]); |
453 | if (value == 90) |
454 | return _gtk_css_value_ref (value: °_singletons[1]); |
455 | if (value == 180) |
456 | return _gtk_css_value_ref (value: °_singletons[2]); |
457 | if (value == 270) |
458 | return _gtk_css_value_ref (value: °_singletons[3]); |
459 | |
460 | break; |
461 | |
462 | default: |
463 | ; |
464 | } |
465 | |
466 | result = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_NUMBER); |
467 | result->type = TYPE_DIMENSION; |
468 | result->dimension.unit = unit; |
469 | result->dimension.value = value; |
470 | result->is_computed = value == 0 || |
471 | unit == GTK_CSS_NUMBER || |
472 | unit == GTK_CSS_PX || |
473 | unit == GTK_CSS_DEG || |
474 | unit == GTK_CSS_S; |
475 | return result; |
476 | } |
477 | |
478 | /* |
479 | * gtk_css_number_value_get_calc_term_order: |
480 | * @value: Value to compute order for |
481 | * |
482 | * Determines the position of @value when printed as part of a calc() |
483 | * expression. Values with lower numbers are printed first. Note that |
484 | * these numbers are arbitrary, so when adding new types of values to |
485 | * print, feel free to change them in implementations so that they |
486 | * match. |
487 | * |
488 | * Returns: Magic value determining placement when printing calc() |
489 | * expression. |
490 | */ |
491 | static int |
492 | gtk_css_number_value_get_calc_term_order (const GtkCssValue *value) |
493 | { |
494 | if (G_LIKELY (value->type == TYPE_DIMENSION)) |
495 | { |
496 | /* note: the order is alphabetic */ |
497 | static const guint order_per_unit[] = { |
498 | /* [GTK_CSS_NUMBER] = */ 0, |
499 | /* [GTK_CSS_PERCENT] = */ 16, |
500 | /* [GTK_CSS_PX] = */ 11, |
501 | /* [GTK_CSS_PT] = */ 10, |
502 | /* [GTK_CSS_EM] = */ 3, |
503 | /* [GTK_CSS_EX] = */ 4, |
504 | /* [GTK_CSS_REM] = */ 13, |
505 | /* [GTK_CSS_PC] = */ 9, |
506 | /* [GTK_CSS_IN] = */ 6, |
507 | /* [GTK_CSS_CM] = */ 1, |
508 | /* [GTK_CSS_MM] = */ 7, |
509 | /* [GTK_CSS_RAD] = */ 12, |
510 | /* [GTK_CSS_DEG] = */ 2, |
511 | /* [GTK_CSS_GRAD] = */ 5, |
512 | /* [GTK_CSS_TURN] = */ 15, |
513 | /* [GTK_CSS_S] = */ 14, |
514 | /* [GTK_CSS_MS] = */ 8 |
515 | }; |
516 | return 1000 + order_per_unit[value->dimension.unit]; |
517 | } |
518 | |
519 | g_assert (value->type == TYPE_CALC); |
520 | |
521 | /* This should never be needed because calc() can't contain calc(), |
522 | * but eh... |
523 | */ |
524 | return 0; |
525 | } |
526 | |
527 | static void |
528 | gtk_css_calc_array_add (GPtrArray *array, GtkCssValue *value) |
529 | { |
530 | gsize i; |
531 | int calc_term_order; |
532 | |
533 | calc_term_order = gtk_css_number_value_get_calc_term_order (value); |
534 | |
535 | for (i = 0; i < array->len; i++) |
536 | { |
537 | GtkCssValue *sum = gtk_css_number_value_try_add (g_ptr_array_index (array, i), value2: value); |
538 | |
539 | if (sum) |
540 | { |
541 | g_ptr_array_index (array, i) = sum; |
542 | _gtk_css_value_unref (value); |
543 | return; |
544 | } |
545 | else if (gtk_css_number_value_get_calc_term_order (g_ptr_array_index (array, i)) > calc_term_order) |
546 | { |
547 | g_ptr_array_insert (array, index_: i, data: value); |
548 | return; |
549 | } |
550 | } |
551 | |
552 | g_ptr_array_add (array, data: value); |
553 | } |
554 | |
555 | static GtkCssValue * |
556 | gtk_css_calc_value_new_sum (GtkCssValue *value1, |
557 | GtkCssValue *value2) |
558 | { |
559 | GPtrArray *array; |
560 | GtkCssValue *result; |
561 | gsize i; |
562 | |
563 | array = g_ptr_array_new (); |
564 | |
565 | if (value1->class == >K_CSS_VALUE_NUMBER && |
566 | value1->type == TYPE_CALC) |
567 | { |
568 | for (i = 0; i < value1->calc.n_terms; i++) |
569 | { |
570 | gtk_css_calc_array_add (array, _gtk_css_value_ref (value: value1->calc.terms[i])); |
571 | } |
572 | } |
573 | else |
574 | { |
575 | gtk_css_calc_array_add (array, _gtk_css_value_ref (value: value1)); |
576 | } |
577 | |
578 | if (value2->class == >K_CSS_VALUE_NUMBER && |
579 | value2->type == TYPE_CALC) |
580 | { |
581 | for (i = 0; i < value2->calc.n_terms; i++) |
582 | { |
583 | gtk_css_calc_array_add (array, _gtk_css_value_ref (value: value2->calc.terms[i])); |
584 | } |
585 | } |
586 | else |
587 | { |
588 | gtk_css_calc_array_add (array, _gtk_css_value_ref (value: value2)); |
589 | } |
590 | |
591 | result = gtk_css_calc_value_new_from_array (values: (GtkCssValue **)array->pdata, n_values: array->len); |
592 | g_ptr_array_free (array, TRUE); |
593 | |
594 | return result; |
595 | } |
596 | |
597 | GtkCssDimension |
598 | gtk_css_number_value_get_dimension (const GtkCssValue *value) |
599 | { |
600 | GtkCssDimension dimension = GTK_CSS_DIMENSION_PERCENTAGE; |
601 | guint i; |
602 | |
603 | if (G_LIKELY (value->type == TYPE_DIMENSION)) |
604 | return gtk_css_unit_get_dimension (unit: value->dimension.unit); |
605 | |
606 | g_assert (value->type == TYPE_CALC); |
607 | |
608 | for (i = 0; i < value->calc.n_terms && dimension == GTK_CSS_DIMENSION_PERCENTAGE; i++) |
609 | { |
610 | dimension = gtk_css_number_value_get_dimension (value: value->calc.terms[i]); |
611 | if (dimension != GTK_CSS_DIMENSION_PERCENTAGE) |
612 | break; |
613 | } |
614 | |
615 | return dimension; |
616 | } |
617 | |
618 | gboolean |
619 | gtk_css_number_value_has_percent (const GtkCssValue *value) |
620 | { |
621 | guint i; |
622 | |
623 | if (G_LIKELY (value->type == TYPE_DIMENSION)) |
624 | { |
625 | return gtk_css_unit_get_dimension (unit: value->dimension.unit) == GTK_CSS_DIMENSION_PERCENTAGE; |
626 | } |
627 | |
628 | for (i = 0; i < value->calc.n_terms; i++) |
629 | { |
630 | if (gtk_css_number_value_has_percent (value: value->calc.terms[i])) |
631 | return TRUE; |
632 | } |
633 | |
634 | return FALSE; |
635 | } |
636 | |
637 | GtkCssValue * |
638 | gtk_css_number_value_multiply (GtkCssValue *value, |
639 | double factor) |
640 | { |
641 | GtkCssValue *result; |
642 | guint i; |
643 | |
644 | if (factor == 1) |
645 | return _gtk_css_value_ref (value); |
646 | |
647 | if (G_LIKELY (value->type == TYPE_DIMENSION)) |
648 | { |
649 | return gtk_css_dimension_value_new (value: value->dimension.value * factor, |
650 | unit: value->dimension.unit); |
651 | } |
652 | |
653 | g_assert (value->type == TYPE_CALC); |
654 | |
655 | result = gtk_css_calc_value_new (n_terms: value->calc.n_terms); |
656 | for (i = 0; i < value->calc.n_terms; i++) |
657 | result->calc.terms[i] = gtk_css_number_value_multiply (value: value->calc.terms[i], factor); |
658 | |
659 | return result; |
660 | } |
661 | |
662 | GtkCssValue * |
663 | gtk_css_number_value_add (GtkCssValue *value1, |
664 | GtkCssValue *value2) |
665 | { |
666 | GtkCssValue *sum; |
667 | |
668 | sum = gtk_css_number_value_try_add (value1, value2); |
669 | if (sum == NULL) |
670 | sum = gtk_css_calc_value_new_sum (value1, value2); |
671 | |
672 | return sum; |
673 | } |
674 | |
675 | GtkCssValue * |
676 | gtk_css_number_value_try_add (GtkCssValue *value1, |
677 | GtkCssValue *value2) |
678 | { |
679 | if (G_UNLIKELY (value1->type != value2->type)) |
680 | return NULL; |
681 | |
682 | if (G_LIKELY (value1->type == TYPE_DIMENSION)) |
683 | { |
684 | if (value1->dimension.unit != value2->dimension.unit) |
685 | return NULL; |
686 | |
687 | if (value1->dimension.value == 0) |
688 | return _gtk_css_value_ref (value: value2); |
689 | |
690 | if (value2->dimension.value == 0) |
691 | return _gtk_css_value_ref (value: value1); |
692 | |
693 | return gtk_css_dimension_value_new (value: value1->dimension.value + value2->dimension.value, |
694 | unit: value1->dimension.unit); |
695 | } |
696 | |
697 | g_assert (value1->type == TYPE_CALC); |
698 | /* Not possible for calc() values */ |
699 | return NULL; |
700 | } |
701 | |
702 | GtkCssValue * |
703 | _gtk_css_number_value_new (double value, |
704 | GtkCssUnit unit) |
705 | { |
706 | return gtk_css_dimension_value_new (value, unit); |
707 | } |
708 | |
709 | gboolean |
710 | gtk_css_number_value_can_parse (GtkCssParser *parser) |
711 | { |
712 | return gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNED_NUMBER) |
713 | || gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNLESS_NUMBER) |
714 | || gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNED_INTEGER) |
715 | || gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNLESS_INTEGER) |
716 | || gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_PERCENTAGE) |
717 | || gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION) |
718 | || gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION) |
719 | || gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNED_DIMENSION) |
720 | || gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNLESS_DIMENSION) |
721 | || gtk_css_parser_has_function (self: parser, name: "calc" ); |
722 | } |
723 | |
724 | GtkCssValue * |
725 | _gtk_css_number_value_parse (GtkCssParser *parser, |
726 | GtkCssNumberParseFlags flags) |
727 | { |
728 | if (gtk_css_parser_has_function (self: parser, name: "calc" )) |
729 | return gtk_css_calc_value_parse (parser, flags); |
730 | |
731 | return gtk_css_dimension_value_parse (parser, flags); |
732 | } |
733 | |
734 | double |
735 | _gtk_css_number_value_get (const GtkCssValue *value, |
736 | double one_hundred_percent) |
737 | { |
738 | double result; |
739 | guint i; |
740 | |
741 | if (G_LIKELY (value->type == TYPE_DIMENSION)) |
742 | { |
743 | if (value->dimension.unit == GTK_CSS_PERCENT) |
744 | return value->dimension.value * one_hundred_percent / 100; |
745 | else |
746 | return value->dimension.value; |
747 | } |
748 | |
749 | g_assert (value->type == TYPE_CALC); |
750 | |
751 | result = 0.0; |
752 | for (i = 0; i < value->calc.n_terms; i++) |
753 | result += _gtk_css_number_value_get (value: value->calc.terms[i], one_hundred_percent); |
754 | |
755 | return result; |
756 | } |
757 | |
758 | gboolean |
759 | gtk_css_dimension_value_is_zero (const GtkCssValue *value) |
760 | { |
761 | if (!value) |
762 | return TRUE; |
763 | |
764 | if (value->class != >K_CSS_VALUE_NUMBER) |
765 | return FALSE; |
766 | |
767 | if (value->type != TYPE_DIMENSION) |
768 | return FALSE; |
769 | |
770 | return value->dimension.value == 0; |
771 | } |
772 | |