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, &GTK_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, &GTK_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 == &GTK_CSS_VALUE_COLOR, NULL);
499 
500   value = _gtk_css_value_new (GtkCssValue, &GTK_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 == &GTK_CSS_VALUE_COLOR, NULL);
515 
516   value = _gtk_css_value_new (GtkCssValue, &GTK_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 == &GTK_CSS_VALUE_COLOR, NULL);
532   gtk_internal_return_val_if_fail (color2->class == &GTK_CSS_VALUE_COLOR, NULL);
533 
534   value = _gtk_css_value_new (GtkCssValue, &GTK_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, &GTK_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 = { &GTK_CSS_VALUE_COLOR, 1, COLOR_TYPE_CURRENT_COLOR, NULL, };
579 
580   return _gtk_css_value_ref (&current_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