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 | |
26 | struct _GtkCssValue { |
27 | GTK_CSS_VALUE_BASE |
28 | }; |
29 | |
30 | G_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 |
35 | static GHashTable *counters; |
36 | |
37 | typedef struct |
38 | { |
39 | guint all; |
40 | guint alive; |
41 | guint computed; |
42 | guint transitioned; |
43 | } ValueAccounting; |
44 | |
45 | static void |
46 | dump_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 | |
118 | static ValueAccounting * |
119 | get_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 | |
139 | GtkCssValue * |
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 | |
163 | GtkCssValue * |
164 | gtk_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 | |
173 | void |
174 | gtk_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 | **/ |
210 | GtkCssValue * |
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 | |
227 | gboolean |
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 | |
243 | gboolean |
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 | |
257 | GtkCssValue * |
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 | |
285 | char * |
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 | **/ |
306 | void |
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 | */ |
329 | gboolean |
330 | gtk_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 | */ |
353 | GtkCssValue * |
354 | gtk_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 | |
365 | gboolean |
366 | gtk_css_value_is_computed (const GtkCssValue *value) |
367 | { |
368 | return value->is_computed; |
369 | } |
370 | |