1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2011 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include "gtkcsspalettevalueprivate.h"
21
22#include "gtkcsscolorvalueprivate.h"
23#include "gtkcsscolorvalueprivate.h"
24#include "gtkprivate.h"
25
26struct _GtkCssValue {
27 GTK_CSS_VALUE_BASE
28 guint n_colors;
29 char **color_names;
30 GtkCssValue **color_values;
31};
32
33static GtkCssValue *default_palette;
34
35static GtkCssValue *gtk_css_palette_value_new_empty (void);
36static GtkCssValue *gtk_css_palette_value_new_sized (guint size);
37
38static void
39gtk_css_palette_value_set_color (GtkCssValue *value,
40 guint i,
41 char *name,
42 GtkCssValue *color)
43{
44 value->color_names[i] = name; /* No strdup */
45 value->color_values[i] = color;
46}
47
48static void
49gtk_css_palette_value_sort_colors (GtkCssValue *value)
50{
51 guint i, j;
52
53 /* Bubble sort. We're mostly talking about 3 elements here. */
54 for (i = 0; i < value->n_colors; i ++)
55 for (j = 0; j < value->n_colors; j ++)
56 {
57 if (strcmp (s1: value->color_names[i], s2: value->color_names[j]) < 0)
58 {
59 char *tmp_name;
60 GtkCssValue *tmp_value;
61
62 tmp_name = value->color_names[i];
63 tmp_value = value->color_values[i];
64
65 value->color_names[i] = value->color_names[j];
66 value->color_values[i] = value->color_values[j];
67
68 value->color_names[j] = tmp_name;
69 value->color_values[j] = tmp_value;
70 }
71 }
72}
73
74static GtkCssValue *
75gtk_css_palette_value_find_color (GtkCssValue *value,
76 const char *color_name)
77{
78 guint i;
79
80 for (i = 0; i < value->n_colors; i ++)
81 {
82 if (strcmp (s1: value->color_names[i], s2: color_name) == 0)
83 return value->color_values[i];
84 }
85
86 return NULL;
87}
88
89static void
90gtk_css_value_palette_free (GtkCssValue *value)
91{
92 guint i;
93
94 for (i = 0; i < value->n_colors; i ++)
95 {
96 g_free (mem: value->color_names[i]);
97 _gtk_css_value_unref (value: value->color_values[i]);
98 }
99
100 g_free (mem: value->color_names);
101 g_free (mem: value->color_values);
102
103 g_slice_free (GtkCssValue, value);
104}
105
106static GtkCssValue *
107gtk_css_value_palette_compute (GtkCssValue *specified,
108 guint property_id,
109 GtkStyleProvider *provider,
110 GtkCssStyle *style,
111 GtkCssStyle *parent_style)
112{
113 GtkCssValue *computed_color;
114 GtkCssValue *result;
115 gboolean changes = FALSE;
116 guint i;
117
118 result = gtk_css_palette_value_new_sized (size: specified->n_colors);
119
120 for (i = 0; i < specified->n_colors; i ++)
121 {
122 GtkCssValue *value = specified->color_values[i];
123
124 computed_color = _gtk_css_value_compute (value, property_id, provider, style, parent_style);
125 result->color_names[i] = g_strdup (str: specified->color_names[i]);
126 result->color_values[i] = computed_color;
127
128 changes |= computed_color != value;
129 }
130
131 if (!changes)
132 {
133 _gtk_css_value_unref (value: result);
134 result = _gtk_css_value_ref (value: specified);
135 }
136
137 return result;
138}
139
140static gboolean
141gtk_css_value_palette_equal (const GtkCssValue *value1,
142 const GtkCssValue *value2)
143{
144 guint i;
145
146 if (value1->n_colors != value2->n_colors)
147 return FALSE;
148
149 for (i = 0; i < value1->n_colors; i ++)
150 {
151 if (strcmp (s1: value1->color_names[i], s2: value2->color_names[i]) != 0)
152 return FALSE;
153
154 if (!_gtk_css_value_equal (value1: value1->color_values[i], value2: value2->color_values[i]))
155 return FALSE;
156 }
157
158 return TRUE;
159}
160
161static GtkCssValue *
162gtk_css_value_palette_transition (GtkCssValue *start,
163 GtkCssValue *end,
164 guint property_id,
165 double progress)
166{
167 GtkCssValue *result, *transition;
168 GtkCssValue *start_color, *end_color;
169 const char *name;
170 guint i;
171 GPtrArray *new_names;
172 GPtrArray *new_values;
173
174 /* XXX: For colors that are only in start or end but not both,
175 * we don't transition but just keep the value.
176 * That causes an abrupt transition to currentColor at the end.
177 */
178
179 result = gtk_css_palette_value_new_empty ();
180 new_names = g_ptr_array_new ();
181 new_values = g_ptr_array_new ();
182
183 for (i = 0; i < start->n_colors; i ++)
184 {
185 name = start->color_names[i];
186 start_color = start->color_values[i];
187 end_color = gtk_css_palette_value_find_color (value: end, color_name: name);
188
189 if (end_color == NULL)
190 transition = _gtk_css_value_ref (value: start_color);
191 else
192 transition = _gtk_css_value_transition (start: start_color, end: end_color, property_id, progress);
193
194 g_ptr_array_add (array: new_names, data: g_strdup (str: name));
195 g_ptr_array_add (array: new_values, data: transition);
196 }
197
198 for (i = 0; i < end->n_colors; i ++)
199 {
200 name = end->color_names[i];
201 end_color = end->color_values[i];
202 start_color = gtk_css_palette_value_find_color (value: start, color_name: name);
203
204 if (start_color != NULL)
205 continue;
206
207 g_ptr_array_add (array: new_names, data: g_strdup (str: name));
208 g_ptr_array_add (array: new_values, _gtk_css_value_ref (value: end_color));
209 }
210
211 result->n_colors = new_names->len;
212 result->color_names = (char **)g_ptr_array_free (array: new_names, FALSE);
213 result->color_values = (GtkCssValue **)g_ptr_array_free (array: new_values, FALSE);
214 gtk_css_palette_value_sort_colors (value: result);
215
216 return result;
217}
218
219static void
220gtk_css_value_palette_print (const GtkCssValue *value,
221 GString *string)
222{
223 gboolean first = TRUE;
224 guint i;
225
226 if (value == default_palette)
227 {
228 g_string_append (string, val: "default");
229 return;
230 }
231
232 for (i = 0; i < value->n_colors; i ++)
233 {
234 if (first)
235 first = FALSE;
236 else
237 g_string_append (string, val: ", ");
238
239 g_string_append (string, val: value->color_names[i]);
240 g_string_append_c (string, ' ');
241 _gtk_css_value_print (value: value->color_values[i], string);
242 }
243}
244
245static const GtkCssValueClass GTK_CSS_VALUE_PALETTE = {
246 "GtkCssPaletteValue",
247 gtk_css_value_palette_free,
248 gtk_css_value_palette_compute,
249 gtk_css_value_palette_equal,
250 gtk_css_value_palette_transition,
251 NULL,
252 NULL,
253 gtk_css_value_palette_print
254};
255
256static GtkCssValue *
257gtk_css_palette_value_new_empty (void)
258{
259 GtkCssValue *result;
260
261 result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_PALETTE);
262
263 return result;
264}
265
266static GtkCssValue *
267gtk_css_palette_value_new_sized (guint size)
268{
269 GtkCssValue *result;
270
271 result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_PALETTE);
272 result->n_colors = size;
273 result->color_names = g_malloc (n_bytes: sizeof (char *) * size);
274 result->color_values = g_malloc (n_bytes: sizeof (GtkCssValue *) * size);
275
276 return result;
277}
278
279GtkCssValue *
280gtk_css_palette_value_new_default (void)
281{
282 if (default_palette == NULL)
283 {
284 default_palette = gtk_css_palette_value_new_sized (size: 3);
285 gtk_css_palette_value_set_color (value: default_palette, i: 0, name: g_strdup (str: "error"),
286 color: _gtk_css_color_value_new_name (name: "error_color"));
287 gtk_css_palette_value_set_color (value: default_palette, i: 1, name: g_strdup (str: "success"),
288 color: _gtk_css_color_value_new_name (name: "success_color"));
289 gtk_css_palette_value_set_color (value: default_palette, i: 2, name: g_strdup (str: "warning"),
290 color: _gtk_css_color_value_new_name (name: "warning_color"));
291 /* Above is already sorted */
292 }
293
294 return _gtk_css_value_ref (value: default_palette);
295}
296
297GtkCssValue *
298gtk_css_palette_value_parse (GtkCssParser *parser)
299{
300 GtkCssValue *result, *color;
301 GPtrArray *names;
302 GPtrArray *colors;
303 char *ident;
304
305 if (gtk_css_parser_try_ident (self: parser, ident: "default"))
306 return gtk_css_palette_value_new_default ();
307
308 result = gtk_css_palette_value_new_empty ();
309 names = g_ptr_array_new ();
310 colors = g_ptr_array_new ();
311
312 do {
313 ident = gtk_css_parser_consume_ident (self: parser);
314 if (ident == NULL)
315 {
316 _gtk_css_value_unref (value: result);
317 return NULL;
318 }
319
320 color = _gtk_css_color_value_parse (parser);
321 if (color == NULL)
322 {
323 g_free (mem: ident);
324 _gtk_css_value_unref (value: result);
325 return NULL;
326 }
327
328 result->is_computed = result->is_computed && gtk_css_value_is_computed (value: color);
329
330 g_ptr_array_add (array: names, data: ident);
331 g_ptr_array_add (array: colors, data: color);
332 } while (gtk_css_parser_try_token (self: parser, token_type: GTK_CSS_TOKEN_COMMA));
333
334 result->n_colors = names->len;
335 result->color_names = (char **)g_ptr_array_free (array: names, FALSE);
336 result->color_values = (GtkCssValue **) g_ptr_array_free (array: colors, FALSE);
337 gtk_css_palette_value_sort_colors (value: result);
338
339 return result;
340}
341
342const GdkRGBA *
343gtk_css_palette_value_get_color (GtkCssValue *value,
344 const char *name)
345{
346 guint i;
347
348 gtk_internal_return_val_if_fail (value->class == &GTK_CSS_VALUE_PALETTE, NULL);
349
350 for (i = 0; i < value->n_colors; i ++)
351 {
352 if (strcmp (s1: value->color_names[i], s2: name) == 0)
353 return gtk_css_color_value_get_rgba (color: value->color_values[i]);
354 }
355
356 return NULL;
357}
358

source code of gtk/gtk/gtkcsspalettevalue.c