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 "gtkprivate.h"
21#include "gtkcssvalueprivate.h"
22
23#include "gtkcssstyleprivate.h"
24#include "gtkstyleproviderprivate.h"
25
26struct _GtkCssValue {
27 GTK_CSS_VALUE_BASE
28};
29
30G_DEFINE_BOXED_TYPE (GtkCssValue, _gtk_css_value, _gtk_css_value_ref, _gtk_css_value_unref)
31
32#undef CSS_VALUE_ACCOUNTING
33
34#ifdef CSS_VALUE_ACCOUNTING
35static GHashTable *counters;
36
37typedef struct
38{
39 guint all;
40 guint alive;
41 guint computed;
42 guint transitioned;
43} ValueAccounting;
44
45static void
46dump_value_counts (void)
47{
48 int col_widths[5] = { 0, strlen ("all"), strlen ("alive"), strlen ("computed"), strlen("transitioned") };
49 GHashTableIter iter;
50 gpointer key;
51 gpointer value;
52 int sum_all = 0, sum_alive = 0, sum_computed = 0, sum_transitioned = 0;
53
54 g_hash_table_iter_init (&iter, counters);
55 while (g_hash_table_iter_next (&iter, &key, &value))
56 {
57 const char *class = key;
58 const ValueAccounting *c = value;
59 char *str;
60
61 sum_all += c->all;
62 sum_alive += c->alive;
63 sum_computed += c->computed;
64 sum_transitioned += c->transitioned;
65
66 col_widths[0] = MAX (col_widths[0], strlen (class));
67
68 str = g_strdup_printf ("%'d", sum_all);
69 col_widths[1] = MAX (col_widths[1], strlen (str));
70 g_free (str);
71
72 str = g_strdup_printf ("%'d", sum_alive);
73 col_widths[2] = MAX (col_widths[2], strlen (str));
74 g_free (str);
75
76 str = g_strdup_printf ("%'d", sum_computed);
77 col_widths[3] = MAX (col_widths[3], strlen (str));
78 g_free (str);
79
80 str = g_strdup_printf ("%'d", sum_transitioned);
81 col_widths[4] = MAX (col_widths[4], strlen (str));
82 g_free (str);
83 }
84 /* Some spacing */
85 col_widths[0] += 4;
86 col_widths[1] += 4;
87 col_widths[2] += 4;
88 col_widths[3] += 4;
89 col_widths[4] += 4;
90
91 g_print("%*s%*s%*s%*s%*s\n", col_widths[0] + 1, " ",
92 col_widths[1] + 1, "All",
93 col_widths[2] + 1, "Alive",
94 col_widths[3] + 1, "Computed",
95 col_widths[4] + 1, "Transitioned");
96
97 g_hash_table_iter_init (&iter, counters);
98 while (g_hash_table_iter_next (&iter, &key, &value))
99 {
100 const char *class = key;
101 const ValueAccounting *c = value;
102
103 g_print ("%*s:", col_widths[0], class);
104 g_print (" %'*d", col_widths[1], c->all);
105 g_print (" %'*d", col_widths[2], c->alive);
106 g_print (" %'*d", col_widths[3], c->computed);
107 g_print (" %'*d", col_widths[4], c->transitioned);
108 g_print("\n");
109 }
110
111 g_print("%*s%'*d%'*d%'*d%'*d\n", col_widths[0] + 1, " ",
112 col_widths[1] + 1, sum_all,
113 col_widths[2] + 1, sum_alive,
114 col_widths[3] + 1, sum_computed,
115 col_widths[4] + 1, sum_transitioned);
116}
117
118static ValueAccounting *
119get_accounting_data (const char *class)
120{
121 ValueAccounting *c;
122
123 if (!counters)
124 {
125 counters = g_hash_table_new (g_str_hash, g_str_equal);
126 atexit (dump_value_counts);
127 }
128 c = g_hash_table_lookup (counters, class);
129 if (!c)
130 {
131 c = g_malloc0 (sizeof (ValueAccounting));
132 g_hash_table_insert (counters, (gpointer)class, c);
133 }
134
135 return c;
136}
137#endif
138
139GtkCssValue *
140_gtk_css_value_alloc (const GtkCssValueClass *klass,
141 gsize size)
142{
143 GtkCssValue *value;
144
145 value = g_slice_alloc0 (block_size: size);
146
147 value->class = klass;
148 value->ref_count = 1;
149
150#ifdef CSS_VALUE_ACCOUNTING
151 {
152 ValueAccounting *c;
153
154 c = get_accounting_data (klass->type_name);
155 c->all++;
156 c->alive++;
157 }
158#endif
159
160 return value;
161}
162
163GtkCssValue *
164gtk_css_value_ref (GtkCssValue *value)
165{
166 gtk_internal_return_val_if_fail (value != NULL, NULL);
167
168 value->ref_count += 1;
169
170 return value;
171}
172
173void
174gtk_css_value_unref (GtkCssValue *value)
175{
176 if (value == NULL)
177 return;
178
179 value->ref_count -= 1;
180 if (value->ref_count > 0)
181 return;
182
183#ifdef CSS_VALUE_ACCOUNTING
184 {
185 ValueAccounting *c;
186
187 c = get_accounting_data (value->class->type_name);
188 c->alive--;
189 }
190#endif
191
192 value->class->free (value);
193}
194
195/**
196 * _gtk_css_value_compute:
197 * @value: the value to compute from
198 * @property_id: the ID of the property to compute
199 * @provider: Style provider for looking up extra information
200 * @style: Style to compute for
201 * @parent_style: parent style to use for inherited values
202 *
203 * Converts the specified @value into the computed value for the CSS
204 * property given by @property_id using the information in @context.
205 * This step is explained in detail in the
206 * [CSS Documentation](http://www.w3.org/TR/css3-cascade/#computed).
207 *
208 * Returns: the computed value
209 **/
210GtkCssValue *
211_gtk_css_value_compute (GtkCssValue *value,
212 guint property_id,
213 GtkStyleProvider *provider,
214 GtkCssStyle *style,
215 GtkCssStyle *parent_style)
216{
217 if (gtk_css_value_is_computed (value))
218 return _gtk_css_value_ref (value);
219
220#ifdef CSS_VALUE_ACCOUNTING
221 get_accounting_data (value->class->type_name)->computed++;
222#endif
223
224 return value->class->compute (value, property_id, provider, style, parent_style);
225}
226
227gboolean
228_gtk_css_value_equal (const GtkCssValue *value1,
229 const GtkCssValue *value2)
230{
231 gtk_internal_return_val_if_fail (value1 != NULL, FALSE);
232 gtk_internal_return_val_if_fail (value2 != NULL, FALSE);
233
234 if (value1 == value2)
235 return TRUE;
236
237 if (value1->class != value2->class)
238 return FALSE;
239
240 return value1->class->equal (value1, value2);
241}
242
243gboolean
244_gtk_css_value_equal0 (const GtkCssValue *value1,
245 const GtkCssValue *value2)
246{
247 /* Includes both values being NULL */
248 if (value1 == value2)
249 return TRUE;
250
251 if (value1 == NULL || value2 == NULL)
252 return FALSE;
253
254 return _gtk_css_value_equal (value1, value2);
255}
256
257GtkCssValue *
258_gtk_css_value_transition (GtkCssValue *start,
259 GtkCssValue *end,
260 guint property_id,
261 double progress)
262{
263 gtk_internal_return_val_if_fail (start != NULL, NULL);
264 gtk_internal_return_val_if_fail (end != NULL, NULL);
265
266 if (start->class != end->class)
267 return NULL;
268
269 if (progress == 0)
270 return _gtk_css_value_ref (value: start);
271
272 if (progress == 1)
273 return _gtk_css_value_ref (value: end);
274
275 if (start == end)
276 return _gtk_css_value_ref (value: start);
277
278#ifdef CSS_VALUE_ACCOUNTING
279 get_accounting_data (start->class->type_name)->transitioned++;
280#endif
281
282 return start->class->transition (start, end, property_id, progress);
283}
284
285char *
286_gtk_css_value_to_string (const GtkCssValue *value)
287{
288 GString *string;
289
290 gtk_internal_return_val_if_fail (value != NULL, NULL);
291
292 string = g_string_new (NULL);
293 _gtk_css_value_print (value, string);
294 return g_string_free (string, FALSE);
295}
296
297/**
298 * _gtk_css_value_print:
299 * @value: the value to print
300 * @string: the string to print to
301 *
302 * Prints @value to the given @string in CSS format. The @value must be a
303 * valid specified value as parsed using the parse functions or as assigned
304 * via _gtk_style_property_assign().
305 **/
306void
307_gtk_css_value_print (const GtkCssValue *value,
308 GString *string)
309{
310 gtk_internal_return_if_fail (value != NULL);
311 gtk_internal_return_if_fail (string != NULL);
312
313 value->class->print (value, string);
314}
315
316/**
317 * gtk_css_value_is_dynamic:
318 * @value: a `GtkCssValue`
319 *
320 * A "dynamic" value has a different value at different times. This means that
321 * the value needs to be animated when time is progressing.
322 *
323 * Examples of dynamic values are animated images, such as videos or dynamic shaders.
324 *
325 * Use gtk_css_value_get_dynamic_value() to get the value for a given timestamp.
326 *
327 * Returns %TRUE if the value is dynamic
328 */
329gboolean
330gtk_css_value_is_dynamic (const GtkCssValue *value)
331{
332 gtk_internal_return_val_if_fail (value != NULL, FALSE);
333
334 if (!value->class->is_dynamic)
335 return FALSE;
336
337 return value->class->is_dynamic (value);
338}
339
340/**
341 * gtk_css_value_get_dynamic_value:
342 * @value: a `GtkCssValue`
343 * @monotonic_time: the timestamp for which to get the dynamic value
344 *
345 * Gets the dynamic value for a given timestamp. If @monotonic_time is 0,
346 * the default value is returned.
347 *
348 * See gtk_css_value_is_dynamic() for details about dynamic values.
349 *
350 * Returns: (transfer full): The dynamic value for @value at the given
351 * timestamp
352 */
353GtkCssValue *
354gtk_css_value_get_dynamic_value (GtkCssValue *value,
355 gint64 monotonic_time)
356{
357 gtk_internal_return_val_if_fail (value != NULL, NULL);
358
359 if (!value->class->get_dynamic_value)
360 return gtk_css_value_ref (value);
361
362 return value->class->get_dynamic_value (value, monotonic_time);
363}
364
365gboolean
366gtk_css_value_is_computed (const GtkCssValue *value)
367{
368 return value->is_computed;
369}
370

source code of gtk/gtk/gtkcssvalue.c