1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Carlos Garnacho <carlosg@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 "gtkcsscolorvalueprivate.h"
21
22 #include "gtkcssrgbavalueprivate.h"
23 #include "gtkcssstylepropertyprivate.h"
24 #include "gtkhslaprivate.h"
25 #include "gtkstylepropertyprivate.h"
26 #include "gtkwin32drawprivate.h"
27 #include "gtkwin32themeprivate.h"
28
29 #include "gtkprivate.h"
30
31 typedef enum {
32 COLOR_TYPE_LITERAL,
33 COLOR_TYPE_NAME,
34 COLOR_TYPE_SHADE,
35 COLOR_TYPE_ALPHA,
36 COLOR_TYPE_MIX,
37 COLOR_TYPE_WIN32,
38 COLOR_TYPE_CURRENT_COLOR
39 } ColorType;
40
41 struct _GtkCssValue
42 {
43 GTK_CSS_VALUE_BASE
44 ColorType type;
45 GtkCssValue *last_value;
46
47 union
48 {
49 gchar *name;
50
51 struct
52 {
53 GtkCssValue *color;
54 gdouble factor;
55 } shade, alpha;
56
57 struct
58 {
59 GtkCssValue *color1;
60 GtkCssValue *color2;
61 gdouble factor;
62 } mix;
63
64 struct
65 {
66 GtkWin32Theme *theme;
67 gint id;
68 } win32;
69 } sym_col;
70 };
71
72 static void
gtk_css_value_color_free(GtkCssValue * color)73 gtk_css_value_color_free (GtkCssValue *color)
74 {
75 if (color->last_value)
76 _gtk_css_value_unref (color->last_value);
77
78 switch (color->type)
79 {
80 case COLOR_TYPE_NAME:
81 g_free (color->sym_col.name);
82 break;
83 case COLOR_TYPE_SHADE:
84 _gtk_css_value_unref (color->sym_col.shade.color);
85 break;
86 case COLOR_TYPE_ALPHA:
87 _gtk_css_value_unref (color->sym_col.alpha.color);
88 break;
89 case COLOR_TYPE_MIX:
90 _gtk_css_value_unref (color->sym_col.mix.color1);
91 _gtk_css_value_unref (color->sym_col.mix.color2);
92 break;
93 case COLOR_TYPE_WIN32:
94 gtk_win32_theme_unref (color->sym_col.win32.theme);
95 break;
96 default:
97 break;
98 }
99
100 g_slice_free (GtkCssValue, color);
101 }
102
103 static GtkCssValue *
gtk_css_value_color_get_fallback(guint property_id,GtkStyleProviderPrivate * provider,GtkCssStyle * style,GtkCssStyle * parent_style)104 gtk_css_value_color_get_fallback (guint property_id,
105 GtkStyleProviderPrivate *provider,
106 GtkCssStyle *style,
107 GtkCssStyle *parent_style)
108 {
109 static const GdkRGBA transparent = { 0, 0, 0, 0 };
110
111 switch (property_id)
112 {
113 case GTK_CSS_PROPERTY_BACKGROUND_IMAGE:
114 case GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE:
115 case GTK_CSS_PROPERTY_TEXT_SHADOW:
116 case GTK_CSS_PROPERTY_ICON_SHADOW:
117 case GTK_CSS_PROPERTY_BOX_SHADOW:
118 return _gtk_css_rgba_value_new_from_rgba (&transparent);
119 case GTK_CSS_PROPERTY_COLOR:
120 case GTK_CSS_PROPERTY_BACKGROUND_COLOR:
121 case GTK_CSS_PROPERTY_BORDER_TOP_COLOR:
122 case GTK_CSS_PROPERTY_BORDER_RIGHT_COLOR:
123 case GTK_CSS_PROPERTY_BORDER_BOTTOM_COLOR:
124 case GTK_CSS_PROPERTY_BORDER_LEFT_COLOR:
125 case GTK_CSS_PROPERTY_OUTLINE_COLOR:
126 case GTK_CSS_PROPERTY_CARET_COLOR:
127 case GTK_CSS_PROPERTY_SECONDARY_CARET_COLOR:
128 return _gtk_css_value_compute (_gtk_css_style_property_get_initial_value (_gtk_css_style_property_lookup_by_id (property_id)),
129 property_id,
130 provider,
131 style,
132 parent_style);
133 case GTK_CSS_PROPERTY_ICON_PALETTE:
134 return _gtk_css_value_ref (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_COLOR));
135 default:
136 if (property_id < GTK_CSS_PROPERTY_N_PROPERTIES)
137 g_warning ("No fallback color defined for property '%s'",
138 _gtk_style_property_get_name (GTK_STYLE_PROPERTY (_gtk_css_style_property_lookup_by_id (property_id))));
139 return _gtk_css_rgba_value_new_from_rgba (&transparent);
140 }
141 }
142
143 GtkCssValue *
_gtk_css_color_value_resolve(GtkCssValue * color,GtkStyleProviderPrivate * provider,GtkCssValue * current,GSList * cycle_list)144 _gtk_css_color_value_resolve (GtkCssValue *color,
145 GtkStyleProviderPrivate *provider,
146 GtkCssValue *current,
147 GSList *cycle_list)
148 {
149 GtkCssValue *value;
150
151 gtk_internal_return_val_if_fail (color != NULL, NULL);
152 gtk_internal_return_val_if_fail (provider == NULL || GTK_IS_STYLE_PROVIDER_PRIVATE (provider), NULL);
153
154 switch (color->type)
155 {
156 case COLOR_TYPE_LITERAL:
157 return _gtk_css_value_ref (color->last_value);
158 case COLOR_TYPE_NAME:
159 {
160 GtkCssValue *named;
161 GSList cycle = { color, cycle_list };
162
163 /* If color exists in cycle_list, we're currently resolving it.
164 * So we've detected a cycle. */
165 if (g_slist_find (cycle_list, color))
166 return NULL;
167
168 named = _gtk_style_provider_private_get_color (provider, color->sym_col.name);
169 if (named == NULL)
170 return NULL;
171
172 value = _gtk_css_color_value_resolve (named, provider, current, &cycle);
173 if (value == NULL)
174 return NULL;
175 }
176
177 break;
178 case COLOR_TYPE_SHADE:
179 {
180 GtkCssValue *val;
181 GtkHSLA hsla;
182 GdkRGBA shade;
183
184 val = _gtk_css_color_value_resolve (color->sym_col.shade.color, provider, current, cycle_list);
185 if (val == NULL)
186 return NULL;
187
188 _gtk_hsla_init_from_rgba (&hsla, _gtk_css_rgba_value_get_rgba (val));
189 _gtk_hsla_shade (&hsla, &hsla, color->sym_col.shade.factor);
190
191 _gdk_rgba_init_from_hsla (&shade, &hsla);
192
193 _gtk_css_value_unref (val);
194
195 value = _gtk_css_rgba_value_new_from_rgba (&shade);
196 }
197
198 break;
199 case COLOR_TYPE_ALPHA:
200 {
201 GtkCssValue *val;
202 GdkRGBA alpha;
203
204 val = _gtk_css_color_value_resolve (color->sym_col.alpha.color, provider, current, cycle_list);
205 if (val == NULL)
206 return NULL;
207
208 alpha = *_gtk_css_rgba_value_get_rgba (val);
209 alpha.alpha = CLAMP (alpha.alpha * color->sym_col.alpha.factor, 0, 1);
210
211 _gtk_css_value_unref (val);
212
213 value = _gtk_css_rgba_value_new_from_rgba (&alpha);
214 }
215 break;
216
217 case COLOR_TYPE_MIX:
218 {
219 GtkCssValue *val;
220 GdkRGBA color1, color2, res;
221
222 val = _gtk_css_color_value_resolve (color->sym_col.mix.color1, provider, current, cycle_list);
223 if (val == NULL)
224 return NULL;
225 color1 = *_gtk_css_rgba_value_get_rgba (val);
226 _gtk_css_value_unref (val);
227
228 val = _gtk_css_color_value_resolve (color->sym_col.mix.color2, provider, current, cycle_list);
229 if (val == NULL)
230 return NULL;
231 color2 = *_gtk_css_rgba_value_get_rgba (val);
232 _gtk_css_value_unref (val);
233
234 res.red = CLAMP (color1.red + ((color2.red - color1.red) * color->sym_col.mix.factor), 0, 1);
235 res.green = CLAMP (color1.green + ((color2.green - color1.green) * color->sym_col.mix.factor), 0, 1);
236 res.blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->sym_col.mix.factor), 0, 1);
237 res.alpha = CLAMP (color1.alpha + ((color2.alpha - color1.alpha) * color->sym_col.mix.factor), 0, 1);
238
239 value =_gtk_css_rgba_value_new_from_rgba (&res);
240 }
241
242 break;
243 case COLOR_TYPE_WIN32:
244 {
245 GdkRGBA res;
246
247 gtk_win32_theme_get_color (color->sym_col.win32.theme,
248 color->sym_col.win32.id,
249 &res);
250
251 value = _gtk_css_rgba_value_new_from_rgba (&res);
252 }
253
254 break;
255 case COLOR_TYPE_CURRENT_COLOR:
256 if (current)
257 {
258 return _gtk_css_value_ref (current);
259 }
260 else
261 {
262 return _gtk_css_color_value_resolve (_gtk_css_style_property_get_initial_value (_gtk_css_style_property_lookup_by_id (GTK_CSS_PROPERTY_COLOR)),
263 provider,
264 NULL,
265 cycle_list);
266 }
267 break;
268 default:
269 value = NULL;
270 g_assert_not_reached ();
271 }
272
273 if (color->last_value != NULL &&
274 _gtk_css_value_equal (color->last_value, value))
275 {
276 _gtk_css_value_unref (value);
277 value = _gtk_css_value_ref (color->last_value);
278 }
279 else
280 {
281 if (color->last_value != NULL)
282 _gtk_css_value_unref (color->last_value);
283 color->last_value = _gtk_css_value_ref (value);
284 }
285
286 return value;
287 }
288
289 static GtkCssValue *
gtk_css_value_color_compute(GtkCssValue * value,guint property_id,GtkStyleProviderPrivate * provider,GtkCssStyle * style,GtkCssStyle * parent_style)290 gtk_css_value_color_compute (GtkCssValue *value,
291 guint property_id,
292 GtkStyleProviderPrivate *provider,
293 GtkCssStyle *style,
294 GtkCssStyle *parent_style)
295 {
296 GtkCssValue *resolved, *current;
297
298 /* The computed value of the ‘currentColor’ keyword is the computed
299 * value of the ‘color’ property. If the ‘currentColor’ keyword is
300 * set on the ‘color’ property itself, it is treated as ‘color: inherit’.
301 */
302 if (property_id == GTK_CSS_PROPERTY_COLOR)
303 {
304 if (parent_style)
305 current = gtk_css_style_get_value (parent_style, GTK_CSS_PROPERTY_COLOR);
306 else
307 current = NULL;
308 }
309 else
310 {
311 current = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_COLOR);
312 }
313
314 resolved = _gtk_css_color_value_resolve (value,
315 provider,
316 current,
317 NULL);
318
319 if (resolved == NULL)
320 return gtk_css_value_color_get_fallback (property_id, provider, style, parent_style);
321
322 return resolved;
323 }
324
325 static gboolean
gtk_css_value_color_equal(const GtkCssValue * value1,const GtkCssValue * value2)326 gtk_css_value_color_equal (const GtkCssValue *value1,
327 const GtkCssValue *value2)
328 {
329 if (value1->type != value2->type)
330 return FALSE;
331
332 switch (value1->type)
333 {
334 case COLOR_TYPE_LITERAL:
335 return _gtk_css_value_equal (value1->last_value, value2->last_value);
336 case COLOR_TYPE_NAME:
337 return g_str_equal (value1->sym_col.name, value2->sym_col.name);
338 case COLOR_TYPE_SHADE:
339 return value1->sym_col.shade.factor == value2->sym_col.shade.factor &&
340 _gtk_css_value_equal (value1->sym_col.shade.color,
341 value2->sym_col.shade.color);
342 case COLOR_TYPE_ALPHA:
343 return value1->sym_col.alpha.factor == value2->sym_col.alpha.factor &&
344 _gtk_css_value_equal (value1->sym_col.alpha.color,
345 value2->sym_col.alpha.color);
346 case COLOR_TYPE_MIX:
347 return value1->sym_col.mix.factor == value2->sym_col.mix.factor &&
348 _gtk_css_value_equal (value1->sym_col.mix.color1,
349 value2->sym_col.mix.color1) &&
350 _gtk_css_value_equal (value1->sym_col.mix.color2,
351 value2->sym_col.mix.color2);
352 case COLOR_TYPE_WIN32:
353 return gtk_win32_theme_equal (value1->sym_col.win32.theme, value2->sym_col.win32.theme) &&
354 value1->sym_col.win32.id == value2->sym_col.win32.id;
355 case COLOR_TYPE_CURRENT_COLOR:
356 return TRUE;
357 default:
358 g_assert_not_reached ();
359 return FALSE;
360 }
361 }
362
363 static GtkCssValue *
gtk_css_value_color_transition(GtkCssValue * start,GtkCssValue * end,guint property_id,double progress)364 gtk_css_value_color_transition (GtkCssValue *start,
365 GtkCssValue *end,
366 guint property_id,
367 double progress)
368 {
369 return _gtk_css_color_value_new_mix (start, end, progress);
370 }
371
372 static void
gtk_css_value_color_print(const GtkCssValue * value,GString * string)373 gtk_css_value_color_print (const GtkCssValue *value,
374 GString *string)
375 {
376 switch (value->type)
377 {
378 case COLOR_TYPE_LITERAL:
379 _gtk_css_value_print (value->last_value, string);
380 break;
381 case COLOR_TYPE_NAME:
382 g_string_append (string, "@");
383 g_string_append (string, value->sym_col.name);
384 break;
385 case COLOR_TYPE_SHADE:
386 {
387 char factor[G_ASCII_DTOSTR_BUF_SIZE];
388
389 g_string_append (string, "shade(");
390 _gtk_css_value_print (value->sym_col.shade.color, string);
391 g_string_append (string, ", ");
392 g_ascii_dtostr (factor, sizeof (factor), value->sym_col.shade.factor);
393 g_string_append (string, factor);
394 g_string_append (string, ")");
395 }
396 break;
397 case COLOR_TYPE_ALPHA:
398 {
399 char factor[G_ASCII_DTOSTR_BUF_SIZE];
400
401 g_string_append (string, "alpha(");
402 _gtk_css_value_print (value->sym_col.alpha.color, string);
403 g_string_append (string, ", ");
404 g_ascii_dtostr (factor, sizeof (factor), value->sym_col.alpha.factor);
405 g_string_append (string, factor);
406 g_string_append (string, ")");
407 }
408 break;
409 case COLOR_TYPE_MIX:
410 {
411 char factor[G_ASCII_DTOSTR_BUF_SIZE];
412
413 g_string_append (string, "mix(");
414 _gtk_css_value_print (value->sym_col.mix.color1, string);
415 g_string_append (string, ", ");
416 _gtk_css_value_print (value->sym_col.mix.color2, string);
417 g_string_append (string, ", ");
418 g_ascii_dtostr (factor, sizeof (factor), value->sym_col.mix.factor);
419 g_string_append (string, factor);
420 g_string_append (string, ")");
421 }
422 break;
423 case COLOR_TYPE_WIN32:
424 {
425 const char *name;
426 g_string_append (string, GTK_WIN32_THEME_SYMBOLIC_COLOR_NAME"(");
427 gtk_win32_theme_print (value->sym_col.win32.theme, string);
428 g_string_append (string, ", ");
429 name = gtk_win32_get_sys_color_name_for_id (value->sym_col.win32.id);
430 if (name)
431 g_string_append (string, name);
432 else
433 g_string_append_printf (string, "%d", value->sym_col.win32.id);
434 g_string_append (string, ")");
435 }
436 break;
437 case COLOR_TYPE_CURRENT_COLOR:
438 g_string_append (string, "currentColor");
439 break;
440 default:
441 g_assert_not_reached ();
442 }
443 }
444
445 static const GtkCssValueClass GTK_CSS_VALUE_COLOR = {
446 gtk_css_value_color_free,
447 gtk_css_value_color_compute,
448 gtk_css_value_color_equal,
449 gtk_css_value_color_transition,
450 gtk_css_value_color_print
451 };
452
453 GtkCssValue *
_gtk_css_color_value_new_literal(const GdkRGBA * color)454 _gtk_css_color_value_new_literal (const GdkRGBA *color)
455 {
456 GtkCssValue *value;
457
458 g_return_val_if_fail (color != NULL, NULL);
459
460 value = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_COLOR);
461 value->type = COLOR_TYPE_LITERAL;
462 value->last_value = _gtk_css_rgba_value_new_from_rgba (color);
463
464 return value;
465 }
466
467 GtkCssValue *
_gtk_css_color_value_new_rgba(double red,double green,double blue,double alpha)468 _gtk_css_color_value_new_rgba (double red,
469 double green,
470 double blue,
471 double alpha)
472 {
473 GdkRGBA rgba = { red, green, blue, alpha };
474
475 return _gtk_css_color_value_new_literal (&rgba);
476 }
477
478 GtkCssValue *
_gtk_css_color_value_new_name(const gchar * name)479 _gtk_css_color_value_new_name (const gchar *name)
480 {
481 GtkCssValue *value;
482
483 gtk_internal_return_val_if_fail (name != NULL, NULL);
484
485 value = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_COLOR);
486 value->type = COLOR_TYPE_NAME;
487 value->sym_col.name = g_strdup (name);
488
489 return value;
490 }
491
492 GtkCssValue *
_gtk_css_color_value_new_shade(GtkCssValue * color,gdouble factor)493 _gtk_css_color_value_new_shade (GtkCssValue *color,
494 gdouble factor)
495 {
496 GtkCssValue *value;
497
498 gtk_internal_return_val_if_fail (color->class == >K_CSS_VALUE_COLOR, NULL);
499
500 value = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_COLOR);
501 value->type = COLOR_TYPE_SHADE;
502 value->sym_col.shade.color = _gtk_css_value_ref (color);
503 value->sym_col.shade.factor = factor;
504
505 return value;
506 }
507
508 GtkCssValue *
_gtk_css_color_value_new_alpha(GtkCssValue * color,gdouble factor)509 _gtk_css_color_value_new_alpha (GtkCssValue *color,
510 gdouble factor)
511 {
512 GtkCssValue *value;
513
514 gtk_internal_return_val_if_fail (color->class == >K_CSS_VALUE_COLOR, NULL);
515
516 value = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_COLOR);
517 value->type = COLOR_TYPE_ALPHA;
518 value->sym_col.alpha.color = _gtk_css_value_ref (color);
519 value->sym_col.alpha.factor = factor;
520
521 return value;
522 }
523
524 GtkCssValue *
_gtk_css_color_value_new_mix(GtkCssValue * color1,GtkCssValue * color2,gdouble factor)525 _gtk_css_color_value_new_mix (GtkCssValue *color1,
526 GtkCssValue *color2,
527 gdouble factor)
528 {
529 GtkCssValue *value;
530
531 gtk_internal_return_val_if_fail (color1->class == >K_CSS_VALUE_COLOR, NULL);
532 gtk_internal_return_val_if_fail (color2->class == >K_CSS_VALUE_COLOR, NULL);
533
534 value = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_COLOR);
535 value->type = COLOR_TYPE_MIX;
536 value->sym_col.mix.color1 = _gtk_css_value_ref (color1);
537 value->sym_col.mix.color2 = _gtk_css_value_ref (color2);
538 value->sym_col.mix.factor = factor;
539
540 return value;
541 }
542
543 static GtkCssValue *
gtk_css_color_value_new_win32_for_theme(GtkWin32Theme * theme,gint id)544 gtk_css_color_value_new_win32_for_theme (GtkWin32Theme *theme,
545 gint id)
546 {
547 GtkCssValue *value;
548
549 gtk_internal_return_val_if_fail (theme != NULL, NULL);
550
551 value = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_COLOR);
552 value->type = COLOR_TYPE_WIN32;
553 value->sym_col.win32.theme = gtk_win32_theme_ref (theme);
554 value->sym_col.win32.id = id;
555
556 return value;
557 }
558
559 GtkCssValue *
_gtk_css_color_value_new_win32(const gchar * theme_class,gint id)560 _gtk_css_color_value_new_win32 (const gchar *theme_class,
561 gint id)
562 {
563 GtkWin32Theme *theme;
564 GtkCssValue *value;
565
566 gtk_internal_return_val_if_fail (theme_class != NULL, NULL);
567
568 theme = gtk_win32_theme_lookup (theme_class);
569 value = gtk_css_color_value_new_win32_for_theme (theme, id);
570 gtk_win32_theme_unref (theme);
571
572 return value;
573 }
574
575 GtkCssValue *
_gtk_css_color_value_new_current_color(void)576 _gtk_css_color_value_new_current_color (void)
577 {
578 static GtkCssValue current_color = { >K_CSS_VALUE_COLOR, 1, COLOR_TYPE_CURRENT_COLOR, NULL, };
579
580 return _gtk_css_value_ref (¤t_color);
581 }
582
583 typedef enum {
584 COLOR_RGBA,
585 COLOR_RGB,
586 COLOR_LIGHTER,
587 COLOR_DARKER,
588 COLOR_SHADE,
589 COLOR_ALPHA,
590 COLOR_MIX,
591 COLOR_WIN32
592 } ColorParseType;
593
594 static GtkCssValue *
gtk_css_color_parse_win32(GtkCssParser * parser)595 gtk_css_color_parse_win32 (GtkCssParser *parser)
596 {
597 GtkCssValue *color;
598 GtkWin32Theme *theme;
599 char *name;
600 int id;
601
602 theme = gtk_win32_theme_parse (parser);
603 if (theme == NULL)
604 return NULL;
605
606 if (! _gtk_css_parser_try (parser, ",", TRUE))
607 {
608 gtk_win32_theme_unref (theme);
609 _gtk_css_parser_error (parser,
610 "Expected ','");
611 return NULL;
612 }
613
614 name = _gtk_css_parser_try_ident (parser, TRUE);
615 if (name)
616 {
617 id = gtk_win32_get_sys_color_id_for_name (name);
618 if (id == -1)
619 {
620 _gtk_css_parser_error (parser, "'%s' is not a win32 color name.", name);
621 g_free (name);
622 return NULL;
623 }
624 g_free (name);
625 }
626 else if (!_gtk_css_parser_try_int (parser, &id))
627 {
628 gtk_win32_theme_unref (theme);
629 _gtk_css_parser_error (parser, "Expected a valid integer value");
630 return NULL;
631 }
632
633 color = gtk_css_color_value_new_win32_for_theme (theme, id);
634 gtk_win32_theme_unref (theme);
635 return color;
636 }
637
638 static GtkCssValue *
_gtk_css_color_value_parse_function(GtkCssParser * parser,ColorParseType color)639 _gtk_css_color_value_parse_function (GtkCssParser *parser,
640 ColorParseType color)
641 {
642 GtkCssValue *value;
643 GtkCssValue *child1, *child2;
644 double d;
645
646 if (!_gtk_css_parser_try (parser, "(", TRUE))
647 {
648 _gtk_css_parser_error (parser, "Missing opening bracket in color definition");
649 return NULL;
650 }
651
652 if (color == COLOR_RGB || color == COLOR_RGBA)
653 {
654 GdkRGBA rgba;
655 double tmp;
656 guint i;
657
658 for (i = 0; i < 3; i++)
659 {
660 if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
661 {
662 _gtk_css_parser_error (parser, "Expected ',' in color definition");
663 return NULL;
664 }
665
666 if (!_gtk_css_parser_try_double (parser, &tmp))
667 {
668 _gtk_css_parser_error (parser, "Invalid number for color value");
669 return NULL;
670 }
671 if (_gtk_css_parser_try (parser, "%", TRUE))
672 tmp /= 100.0;
673 else
674 tmp /= 255.0;
675 if (i == 0)
676 rgba.red = tmp;
677 else if (i == 1)
678 rgba.green = tmp;
679 else if (i == 2)
680 rgba.blue = tmp;
681 else
682 g_assert_not_reached ();
683 }
684
685 if (color == COLOR_RGBA)
686 {
687 if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
688 {
689 _gtk_css_parser_error (parser, "Expected ',' in color definition");
690 return NULL;
691 }
692
693 if (!_gtk_css_parser_try_double (parser, &rgba.alpha))
694 {
695 _gtk_css_parser_error (parser, "Invalid number for alpha value");
696 return NULL;
697 }
698 }
699 else
700 rgba.alpha = 1.0;
701
702 value = _gtk_css_color_value_new_literal (&rgba);
703 }
704 else if (color == COLOR_WIN32)
705 {
706 value = gtk_css_color_parse_win32 (parser);
707 if (value == NULL)
708 return NULL;
709 }
710 else
711 {
712 child1 = _gtk_css_color_value_parse (parser);
713 if (child1 == NULL)
714 return NULL;
715
716 if (color == COLOR_MIX)
717 {
718 if (!_gtk_css_parser_try (parser, ",", TRUE))
719 {
720 _gtk_css_parser_error (parser, "Expected ',' in color definition");
721 _gtk_css_value_unref (child1);
722 return NULL;
723 }
724
725 child2 = _gtk_css_color_value_parse (parser);
726 if (child2 == NULL)
727 {
728 _gtk_css_value_unref (child1);
729 return NULL;
730 }
731 }
732 else
733 child2 = NULL;
734
735 if (color == COLOR_LIGHTER)
736 d = 1.3;
737 else if (color == COLOR_DARKER)
738 d = 0.7;
739 else
740 {
741 if (!_gtk_css_parser_try (parser, ",", TRUE))
742 {
743 _gtk_css_parser_error (parser, "Expected ',' in color definition");
744 _gtk_css_value_unref (child1);
745 if (child2)
746 _gtk_css_value_unref (child2);
747 return NULL;
748 }
749
750 if (!_gtk_css_parser_try_double (parser, &d))
751 {
752 _gtk_css_parser_error (parser, "Expected number in color definition");
753 _gtk_css_value_unref (child1);
754 if (child2)
755 _gtk_css_value_unref (child2);
756 return NULL;
757 }
758 }
759
760 switch (color)
761 {
762 case COLOR_LIGHTER:
763 case COLOR_DARKER:
764 case COLOR_SHADE:
765 value = _gtk_css_color_value_new_shade (child1, d);
766 break;
767 case COLOR_ALPHA:
768 value = _gtk_css_color_value_new_alpha (child1, d);
769 break;
770 case COLOR_MIX:
771 value = _gtk_css_color_value_new_mix (child1, child2, d);
772 break;
773 default:
774 g_assert_not_reached ();
775 value = NULL;
776 }
777
778 _gtk_css_value_unref (child1);
779 if (child2)
780 _gtk_css_value_unref (child2);
781 }
782
783 if (!_gtk_css_parser_try (parser, ")", TRUE))
784 {
785 _gtk_css_parser_error (parser, "Expected ')' in color definition");
786 _gtk_css_value_unref (value);
787 return NULL;
788 }
789
790 return value;
791 }
792
793 GtkCssValue *
_gtk_css_color_value_parse(GtkCssParser * parser)794 _gtk_css_color_value_parse (GtkCssParser *parser)
795 {
796 GtkCssValue *value;
797 GdkRGBA rgba;
798 guint color;
799 const char *names[] = {"rgba", "rgb", "lighter", "darker", "shade", "alpha", "mix",
800 GTK_WIN32_THEME_SYMBOLIC_COLOR_NAME};
801 char *name;
802
803 if (_gtk_css_parser_try (parser, "currentColor", TRUE))
804 return _gtk_css_color_value_new_current_color ();
805
806 if (_gtk_css_parser_try (parser, "transparent", TRUE))
807 {
808 GdkRGBA transparent = { 0, 0, 0, 0 };
809
810 return _gtk_css_color_value_new_literal (&transparent);
811 }
812
813 if (_gtk_css_parser_try (parser, "@", FALSE))
814 {
815 name = _gtk_css_parser_try_name (parser, TRUE);
816
817 if (name)
818 {
819 value = _gtk_css_color_value_new_name (name);
820 }
821 else
822 {
823 _gtk_css_parser_error (parser, "'%s' is not a valid color color name", name);
824 value = NULL;
825 }
826
827 g_free (name);
828 return value;
829 }
830
831 for (color = 0; color < G_N_ELEMENTS (names); color++)
832 {
833 if (_gtk_css_parser_try (parser, names[color], TRUE))
834 break;
835 }
836
837 if (color < G_N_ELEMENTS (names))
838 return _gtk_css_color_value_parse_function (parser, color);
839
840 if (_gtk_css_parser_try_hash_color (parser, &rgba))
841 return _gtk_css_color_value_new_literal (&rgba);
842
843 name = _gtk_css_parser_try_name (parser, TRUE);
844 if (name)
845 {
846 if (gdk_rgba_parse (&rgba, name))
847 {
848 value = _gtk_css_color_value_new_literal (&rgba);
849 }
850 else
851 {
852 _gtk_css_parser_error (parser, "'%s' is not a valid color name", name);
853 value = NULL;
854 }
855 g_free (name);
856 return value;
857 }
858
859 _gtk_css_parser_error (parser, "Not a color definition");
860 return NULL;
861 }
862
863