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 
G_DEFINE_BOXED_TYPE(GtkCssValue,_gtk_css_value,_gtk_css_value_ref,_gtk_css_value_unref)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
dump_value_counts(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 *
get_accounting_data(const char * class)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 *
_gtk_css_value_alloc(const GtkCssValueClass * klass,gsize size)140 _gtk_css_value_alloc (const GtkCssValueClass *klass,
141                       gsize                   size)
142 {
143   GtkCssValue *value;
144 
145   value = g_slice_alloc0 (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 *
gtk_css_value_ref(GtkCssValue * value)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
gtk_css_value_unref(GtkCssValue * value)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 *
_gtk_css_value_compute(GtkCssValue * value,guint property_id,GtkStyleProvider * provider,GtkCssStyle * style,GtkCssStyle * parent_style)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
_gtk_css_value_equal(const GtkCssValue * value1,const GtkCssValue * value2)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
_gtk_css_value_equal0(const GtkCssValue * value1,const GtkCssValue * value2)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 *
_gtk_css_value_transition(GtkCssValue * start,GtkCssValue * end,guint property_id,double progress)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 (start);
271 
272   if (progress == 1)
273     return _gtk_css_value_ref (end);
274 
275   if (start == end)
276     return _gtk_css_value_ref (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 *
_gtk_css_value_to_string(const GtkCssValue * value)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
_gtk_css_value_print(const GtkCssValue * value,GString * string)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
gtk_css_value_is_dynamic(const GtkCssValue * value)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 *
gtk_css_value_get_dynamic_value(GtkCssValue * value,gint64 monotonic_time)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
gtk_css_value_is_computed(const GtkCssValue * value)366 gtk_css_value_is_computed (const GtkCssValue *value)
367 {
368   return value->is_computed;
369 }
370