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 | |
26 | struct _GtkCssValue { |
27 | GTK_CSS_VALUE_BASE |
28 | guint n_colors; |
29 | char **color_names; |
30 | GtkCssValue **color_values; |
31 | }; |
32 | |
33 | static GtkCssValue *default_palette; |
34 | |
35 | static GtkCssValue *gtk_css_palette_value_new_empty (void); |
36 | static GtkCssValue *gtk_css_palette_value_new_sized (guint size); |
37 | |
38 | static void |
39 | gtk_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 | |
48 | static void |
49 | gtk_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 | |
74 | static GtkCssValue * |
75 | gtk_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 | |
89 | static void |
90 | gtk_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 | |
106 | static GtkCssValue * |
107 | gtk_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 | |
140 | static gboolean |
141 | gtk_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 | |
161 | static GtkCssValue * |
162 | gtk_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 | |
219 | static void |
220 | gtk_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 | |
245 | static 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 | |
256 | static GtkCssValue * |
257 | gtk_css_palette_value_new_empty (void) |
258 | { |
259 | GtkCssValue *result; |
260 | |
261 | result = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_PALETTE); |
262 | |
263 | return result; |
264 | } |
265 | |
266 | static GtkCssValue * |
267 | gtk_css_palette_value_new_sized (guint size) |
268 | { |
269 | GtkCssValue *result; |
270 | |
271 | result = _gtk_css_value_new (GtkCssValue, >K_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 | |
279 | GtkCssValue * |
280 | gtk_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 | |
297 | GtkCssValue * |
298 | gtk_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 | |
342 | const GdkRGBA * |
343 | gtk_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 == >K_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 | |