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
28static GtkCssValue * gtk_css_calc_value_new (guint n_terms);
29static GtkCssValue * gtk_css_calc_value_new_sum (GtkCssValue *a,
30 GtkCssValue *b);
31static gsize gtk_css_value_calc_get_size (gsize n_terms);
32
33enum {
34 TYPE_CALC = 0,
35 TYPE_DIMENSION = 1,
36};
37
38struct _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
54static GtkCssValue *
55gtk_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
73static void
74gtk_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
91static double
92get_dpi (GtkCssStyle *style)
93{
94 return _gtk_css_number_value_get (number: style->core->dpi, one_hundred_percent: 96);
95}
96
97static double
98get_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
114static GtkCssValue *
115gtk_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
219static gboolean
220gtk_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
248static void
249gtk_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
303static GtkCssValue *
304gtk_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
336static 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
347static gsize
348gtk_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
355static GtkCssValue *
356gtk_css_calc_value_new (guint n_terms)
357{
358 GtkCssValue *result;
359
360 result = _gtk_css_value_alloc (klass: &GTK_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
368GtkCssValue *
369gtk_css_dimension_value_new (double value,
370 GtkCssUnit unit)
371{
372 static GtkCssValue number_singletons[] = {
373 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_NUMBER, 0 }} },
374 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_NUMBER, 1 }} },
375 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_NUMBER, 96 }} }, /* DPI default */
376 };
377 static GtkCssValue px_singletons[] = {
378 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 0 }} },
379 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 1 }} },
380 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 2 }} },
381 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 3 }} },
382 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 4 }} },
383 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 8 }} },
384 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 16 }} }, /* Icon size default */
385 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 32 }} },
386 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PX, 64 }} },
387 };
388 static GtkCssValue percent_singletons[] = {
389 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PERCENT, 0 }} },
390 { &GTK_CSS_VALUE_NUMBER, 1, FALSE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PERCENT, 50 }} },
391 { &GTK_CSS_VALUE_NUMBER, 1, FALSE, TYPE_DIMENSION, {.dimension: { GTK_CSS_PERCENT, 100 }} },
392 };
393 static GtkCssValue second_singletons[] = {
394 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_S, 0 }} },
395 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_S, 1 }} },
396 };
397 static GtkCssValue deg_singletons[] = {
398 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_DEG, 0 }} },
399 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_DEG, 90 }} },
400 { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {.dimension: { GTK_CSS_DEG, 180 }} },
401 { &GTK_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: &deg_singletons[0]);
453 if (value == 90)
454 return _gtk_css_value_ref (value: &deg_singletons[1]);
455 if (value == 180)
456 return _gtk_css_value_ref (value: &deg_singletons[2]);
457 if (value == 270)
458 return _gtk_css_value_ref (value: &deg_singletons[3]);
459
460 break;
461
462 default:
463 ;
464 }
465
466 result = _gtk_css_value_new (GtkCssValue, &GTK_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 */
491static int
492gtk_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
527static void
528gtk_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
555static GtkCssValue *
556gtk_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 == &GTK_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 == &GTK_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
597GtkCssDimension
598gtk_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
618gboolean
619gtk_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
637GtkCssValue *
638gtk_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
662GtkCssValue *
663gtk_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
675GtkCssValue *
676gtk_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
702GtkCssValue *
703_gtk_css_number_value_new (double value,
704 GtkCssUnit unit)
705{
706 return gtk_css_dimension_value_new (value, unit);
707}
708
709gboolean
710gtk_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
724GtkCssValue *
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
734double
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
758gboolean
759gtk_css_dimension_value_is_zero (const GtkCssValue *value)
760{
761 if (!value)
762 return TRUE;
763
764 if (value->class != &GTK_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

source code of gtk/gtk/gtkcssnumbervalue.c