]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssvalue.c
b79c4ee180fefc94a53d92a3a369f49006066e60
[~andy/gtk] / gtk / gtkcssvalue.c
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 "gtkcssvalueprivate.h"
21 #include "gtkcssstylefuncsprivate.h"
22 #include "gtktypebuiltins.h"
23 #include "gtkgradient.h"
24 #include <cairo-gobject.h>
25 #include "gtkprivatetypebuiltins.h"
26
27 #include "fallback-c89.c"
28
29 struct _GtkCssValue
30 {
31   GTK_CSS_VALUE_BASE
32   GType type;
33   union {
34     gpointer ptr;
35     gint gint;
36     guint guint;
37     double dbl;
38     float flt;
39   } u;
40 };
41
42 static void
43 gtk_css_value_default_free (GtkCssValue *value)
44 {
45   GType type = value->type;
46
47   if (g_type_is_a (type, G_TYPE_OBJECT))
48     {
49       if (value->u.ptr != NULL)
50         g_object_unref (value->u.ptr);
51     }
52   else if (g_type_is_a (type, G_TYPE_BOXED))
53     {
54       if (value->u.ptr != NULL)
55         g_boxed_free (type, value->u.ptr);
56     }
57   else if (g_type_is_a (type, G_TYPE_STRING))
58     g_free (value->u.ptr);
59   else if (g_type_is_a (type, G_TYPE_INT))
60     {}
61   else if (g_type_is_a (type, G_TYPE_UINT))
62     {}
63   else if (g_type_is_a (type, G_TYPE_BOOLEAN))
64     {}
65   else if (g_type_is_a (type, G_TYPE_ENUM))
66     {}
67   else if (g_type_is_a (type, G_TYPE_FLAGS))
68     {}
69   else if (g_type_is_a (type, G_TYPE_DOUBLE))
70     {}
71   else if (g_type_is_a (type, G_TYPE_FLOAT))
72     {}
73   else
74     {
75       g_value_unset (value->u.ptr);
76       g_slice_free (GValue, value->u.ptr);
77     }
78
79   g_slice_free (GtkCssValue, value);
80 }
81
82 static void
83 gtk_css_value_default_print (const GtkCssValue *value,
84                              GString           *string)
85 {
86   GValue g_value = G_VALUE_INIT;
87
88   _gtk_css_value_init_gvalue (value, &g_value);
89   _gtk_css_style_print_value (&g_value, string);
90   g_value_unset (&g_value);
91 }
92
93 static const GtkCssValueClass GTK_CSS_VALUE_DEFAULT = {
94   gtk_css_value_default_free,
95   gtk_css_value_default_print
96 };
97
98 G_DEFINE_BOXED_TYPE (GtkCssValue, _gtk_css_value, _gtk_css_value_ref, _gtk_css_value_unref)
99
100 static GtkCssValue *
101 gtk_css_value_new (GType type)
102 {
103   GtkCssValue *value;
104
105   value = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_DEFAULT);
106
107   value->type = type;
108
109   return value;
110 }
111
112 GtkCssValue *
113 _gtk_css_value_new_from_gvalue (const GValue *g_value)
114 {
115   GtkCssValue *value;
116   GType type;
117
118   g_return_val_if_fail (g_value != NULL, NULL);
119
120   type = G_VALUE_TYPE (g_value);
121
122   /* Make sure we reuse the int/number singletons */
123   if (type == G_TYPE_INT)
124     value = _gtk_css_value_new_from_int (g_value_get_int (g_value));
125   else if (type == GTK_TYPE_CSS_NUMBER)
126     value = _gtk_css_value_new_from_number (g_value_get_boxed (g_value));
127   else
128     {
129       value = gtk_css_value_new (type);
130
131       if (g_type_is_a (type, G_TYPE_OBJECT))
132         value->u.ptr = g_value_dup_object (g_value);
133       else if (g_type_is_a (type, G_TYPE_BOXED))
134         value->u.ptr = g_value_dup_boxed (g_value);
135       else if (g_type_is_a (type, G_TYPE_INT))
136         value->u.gint = g_value_get_int (g_value);
137       else if (g_type_is_a (type, G_TYPE_UINT))
138         value->u.guint = g_value_get_uint (g_value);
139       else if (g_type_is_a (type, G_TYPE_BOOLEAN))
140         value->u.gint = g_value_get_boolean (g_value);
141       else if (g_type_is_a (type, G_TYPE_ENUM))
142         value->u.gint = g_value_get_enum (g_value);
143       else if (g_type_is_a (type, G_TYPE_FLAGS))
144         value->u.guint = g_value_get_flags (g_value);
145       else if (g_type_is_a (type, G_TYPE_STRING))
146         value->u.ptr = g_value_dup_string (g_value);
147       else if (g_type_is_a (type, G_TYPE_DOUBLE))
148         value->u.dbl = g_value_get_double (g_value);
149       else if (g_type_is_a (type, G_TYPE_FLOAT))
150         value->u.flt = g_value_get_float (g_value);
151       else
152         {
153           value->u.ptr = g_slice_new0 (GValue);
154           g_value_init (value->u.ptr, G_VALUE_TYPE (g_value));
155           g_value_copy (g_value, value->u.ptr);
156         }
157     }
158
159   return value;
160 }
161
162 GtkCssValue *
163 _gtk_css_value_new_take_gvalue (GValue *g_value)
164 {
165   GtkCssValue *value;
166   GType type;
167
168   g_return_val_if_fail (g_value != NULL, NULL);
169
170   type = G_VALUE_TYPE (g_value);
171
172   /* Make sure we reuse the int/number singletons */
173   if (type == G_TYPE_INT)
174     {
175       value = _gtk_css_value_new_from_int (g_value_get_int (g_value));
176       g_value_unset (g_value);
177     }
178   else if (type == GTK_TYPE_CSS_NUMBER)
179     {
180       value = _gtk_css_value_new_from_number (g_value_get_boxed (g_value));
181       g_value_unset (g_value);
182     }
183   else
184     {
185       value = gtk_css_value_new (type);
186
187       if (g_type_is_a (type, G_TYPE_OBJECT))
188         value->u.ptr = g_value_get_object (g_value);
189       else if (g_type_is_a (type, G_TYPE_BOXED))
190         value->u.ptr = g_value_get_boxed (g_value);
191       else if (g_type_is_a (type, G_TYPE_INT))
192         value->u.gint = g_value_get_int (g_value);
193       else if (g_type_is_a (type, G_TYPE_UINT))
194         value->u.guint = g_value_get_uint (g_value);
195       else if (g_type_is_a (type, G_TYPE_BOOLEAN))
196         value->u.gint = g_value_get_boolean (g_value);
197       else if (g_type_is_a (type, G_TYPE_ENUM))
198         value->u.gint = g_value_get_enum (g_value);
199       else if (g_type_is_a (type, G_TYPE_FLAGS))
200         value->u.guint = g_value_get_flags (g_value);
201       else if (g_type_is_a (type, G_TYPE_STRING))
202         value->u.ptr = g_value_dup_string (g_value);
203       else if (g_type_is_a (type, G_TYPE_DOUBLE))
204         value->u.dbl = g_value_get_double (g_value);
205       else if (g_type_is_a (type, G_TYPE_FLOAT))
206         value->u.flt = g_value_get_float (g_value);
207       else
208         {
209           value->u.ptr = g_slice_new0 (GValue);
210           g_value_init (value->u.ptr, G_VALUE_TYPE (g_value));
211           g_value_copy (g_value, value->u.ptr);
212           g_value_unset (g_value);
213         }
214     }
215
216   return value;
217 }
218
219 GtkCssValue *
220 _gtk_css_value_new_from_int (gint val)
221 {
222   GtkCssValue *value;
223   static GtkCssValue *singletons[4] = {NULL};
224
225   if (val >= 0 && val < G_N_ELEMENTS (singletons))
226     {
227       if (singletons[val] == NULL)
228         {
229           value = gtk_css_value_new (G_TYPE_INT);
230           value->u.gint = val;
231           singletons[val] = value;
232         }
233       return _gtk_css_value_ref (singletons[val]);
234     }
235
236   value = gtk_css_value_new (G_TYPE_INT);
237   value->u.gint = val;
238
239   return value;
240 }
241
242 GtkCssValue *
243 _gtk_css_value_new_take_string (char *string)
244 {
245   GtkCssValue *value;
246
247   value = gtk_css_value_new (G_TYPE_STRING);
248   value->u.ptr = string;
249
250   return value;
251 }
252
253 static gpointer
254 g_boxed_copy0 (GType         boxed_type,
255                gconstpointer src_boxed)
256 {
257   if (src_boxed == NULL)
258     return NULL;
259   return g_boxed_copy (boxed_type, src_boxed);
260 }
261
262 GtkCssValue *
263 _gtk_css_value_new_take_pattern (cairo_pattern_t *v)
264 {
265   GtkCssValue *value;
266
267   value = gtk_css_value_new (CAIRO_GOBJECT_TYPE_PATTERN);
268   value->u.ptr = v;
269
270   return value;
271 }
272
273 GtkCssValue *
274 _gtk_css_value_new_take_shadow (GtkShadow *v)
275 {
276   GtkCssValue *value;
277
278   value = gtk_css_value_new (GTK_TYPE_SHADOW);
279   value->u.ptr = v;
280
281   return value;
282 }
283
284 GtkCssValue *
285 _gtk_css_value_new_take_image (GtkCssImage *v)
286 {
287   GtkCssValue *value;
288
289   value = gtk_css_value_new (GTK_TYPE_CSS_IMAGE);
290   value->u.ptr = v;
291
292   return value;
293 }
294
295 GtkCssValue *
296 _gtk_css_value_new_from_number (const GtkCssNumber *v)
297 {
298   GtkCssValue *value;
299   static GtkCssValue *zero_singleton = NULL;
300   static GtkCssValue *px_singletons[5] = {NULL};
301
302   if (v->unit == GTK_CSS_NUMBER &&
303       v->value == 0)
304     {
305       if (zero_singleton == NULL)
306         {
307           value = gtk_css_value_new (GTK_TYPE_CSS_NUMBER);
308           value->u.ptr = g_boxed_copy0 (GTK_TYPE_CSS_NUMBER, v);
309           zero_singleton = value;
310         }
311       return _gtk_css_value_ref (zero_singleton);
312     }
313
314   if (v->unit == GTK_CSS_PX &&
315       (v->value == 0 ||
316        v->value == 1 ||
317        v->value == 2 ||
318        v->value == 3 ||
319        v->value == 4))
320     {
321       int i = round (v->value);
322       if (px_singletons[i] == NULL)
323         {
324           value = gtk_css_value_new (GTK_TYPE_CSS_NUMBER);
325           value->u.ptr = g_boxed_copy0 (GTK_TYPE_CSS_NUMBER, v);
326           px_singletons[i] = value;
327         }
328
329       return _gtk_css_value_ref (px_singletons[i]);
330     }
331
332   value = gtk_css_value_new (GTK_TYPE_CSS_NUMBER);
333   value->u.ptr = g_boxed_copy0 (GTK_TYPE_CSS_NUMBER, v);
334
335   return value;
336 }
337
338 GtkCssValue *
339 _gtk_css_value_new_from_rgba (const GdkRGBA *v)
340 {
341   GtkCssValue *value;
342
343   value = gtk_css_value_new (GDK_TYPE_RGBA);
344   value->u.ptr = g_boxed_copy0 (GDK_TYPE_RGBA, v);
345
346   return value;
347 }
348
349 GtkCssValue *
350 _gtk_css_value_new_from_color (const GdkColor *v)
351 {
352   GtkCssValue *value;
353
354   value = gtk_css_value_new (GDK_TYPE_COLOR);
355   value->u.ptr = g_boxed_copy0 (GDK_TYPE_COLOR, v);
356
357   return value;
358 }
359
360 GtkCssValue *
361 _gtk_css_value_new_from_background_size (const GtkCssBackgroundSize *v)
362 {
363   GtkCssValue *value;
364
365   value = gtk_css_value_new (GTK_TYPE_CSS_BACKGROUND_SIZE);
366   value->u.ptr = g_boxed_copy0 (GTK_TYPE_CSS_BACKGROUND_SIZE, v);
367
368   return value;
369 }
370
371 GtkCssValue *
372 _gtk_css_value_new_from_background_position (const GtkCssBackgroundPosition *v)
373 {
374   GtkCssValue *value;
375
376   value = gtk_css_value_new (GTK_TYPE_CSS_BACKGROUND_POSITION);
377   value->u.ptr = g_boxed_copy0 (GTK_TYPE_CSS_BACKGROUND_POSITION, v);
378
379   return value;
380 }
381
382 GtkCssValue *
383 _gtk_css_value_new_take_symbolic_color (GtkSymbolicColor *v)
384 {
385   GtkCssValue *value;
386
387   value = gtk_css_value_new (GTK_TYPE_SYMBOLIC_COLOR);
388   value->u.ptr = v;
389
390   return value;
391 }
392
393 GtkCssValue *
394 _gtk_css_value_alloc (const GtkCssValueClass *klass,
395                       gsize                   size)
396 {
397   GtkCssValue *value;
398
399   value = g_slice_alloc0 (size);
400
401   value->class = klass;
402   value->ref_count = 1;
403
404   return value;
405 }
406
407 GtkCssValue *
408 _gtk_css_value_ref (GtkCssValue *value)
409 {
410   g_return_val_if_fail (value != NULL, NULL);
411
412   g_atomic_int_add (&value->ref_count, 1);
413
414   return value;
415 }
416
417 void
418 _gtk_css_value_unref (GtkCssValue *value)
419 {
420   if (value == NULL)
421     return;
422
423   if (!g_atomic_int_dec_and_test (&value->ref_count))
424     return;
425
426   value->class->free (value);
427 }
428
429 void
430 _gtk_css_value_print (const GtkCssValue *value,
431                       GString           *string)
432 {
433   g_return_if_fail (value != NULL);
434   g_return_if_fail (string != NULL);
435
436   value->class->print (value, string);
437 }
438
439 GType
440 _gtk_css_value_get_content_type (const GtkCssValue *value)
441 {
442   return value->type;
443 }
444
445 gboolean
446 _gtk_css_value_holds (const GtkCssValue *value, GType type)
447 {
448   return g_type_is_a (value->type, type);
449 }
450
451 static void
452 fill_gvalue (const GtkCssValue *value,
453              GValue            *g_value)
454 {
455   GType type;
456
457   type = value->type;
458
459   if (g_type_is_a (type, G_TYPE_OBJECT))
460     g_value_set_object (g_value, value->u.ptr);
461   else if (g_type_is_a (type, G_TYPE_BOXED))
462     g_value_set_boxed (g_value, value->u.ptr);
463   else if (g_type_is_a (type, G_TYPE_INT))
464     g_value_set_int (g_value, value->u.gint);
465   else if (g_type_is_a (type, G_TYPE_UINT))
466     g_value_set_uint (g_value, value->u.guint);
467   else if (g_type_is_a (type, G_TYPE_BOOLEAN))
468     g_value_set_boolean (g_value, value->u.gint);
469   else if (g_type_is_a (type, G_TYPE_ENUM))
470     g_value_set_enum (g_value, value->u.gint);
471   else if (g_type_is_a (type, G_TYPE_FLAGS))
472     g_value_set_flags (g_value, value->u.guint);
473   else if (g_type_is_a (type, G_TYPE_STRING))
474     g_value_set_string (g_value, value->u.ptr);
475   else if (g_type_is_a (type, G_TYPE_DOUBLE))
476     g_value_set_double (g_value, value->u.dbl);
477   else if (g_type_is_a (type, G_TYPE_FLOAT))
478     g_value_set_float (g_value, value->u.flt);
479   else
480     g_value_copy (value->u.ptr, g_value);
481 }
482
483 void
484 _gtk_css_value_init_gvalue (const GtkCssValue *value,
485                             GValue            *g_value)
486 {
487   if (value != NULL)
488     {
489       g_value_init (g_value, value->type);
490       fill_gvalue (value, g_value);
491     }
492 }
493
494 gboolean
495 _gtk_css_value_is_special (const GtkCssValue *value)
496 {
497   return _gtk_css_value_holds (value, GTK_TYPE_CSS_SPECIAL_VALUE);
498 }
499
500 GtkCssSpecialValue
501 _gtk_css_value_get_special_kind (const GtkCssValue *value)
502 {
503   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_SPECIAL_VALUE), 0);
504   return value->u.gint;
505 }
506
507 const GtkCssNumber *
508 _gtk_css_value_get_number (const GtkCssValue *value)
509 {
510   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_NUMBER), NULL);
511   return value->u.ptr;
512 }
513
514 GtkSymbolicColor *
515 _gtk_css_value_get_symbolic_color (const GtkCssValue *value)
516 {
517   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_SYMBOLIC_COLOR), NULL);
518   return value->u.ptr;
519 }
520
521 int
522 _gtk_css_value_get_int (const GtkCssValue *value)
523 {
524   g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_INT), 0);
525   return value->u.gint;
526 }
527
528 int
529 _gtk_css_value_get_enum (const GtkCssValue *value)
530 {
531   g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_ENUM), 0);
532   return value->u.gint;
533 }
534
535 double
536 _gtk_css_value_get_double (const GtkCssValue *value)
537 {
538   g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_DOUBLE), 0);
539   return value->u.dbl;
540 }
541
542 const char *
543 _gtk_css_value_get_string (const GtkCssValue *value)
544 {
545   g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_STRING), 0);
546   return value->u.ptr;
547 }
548
549 gpointer
550 _gtk_css_value_dup_object (const GtkCssValue *value)
551 {
552   g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_OBJECT), NULL);
553   if (value->u.ptr)
554     return g_object_ref (value->u.ptr);
555   return NULL;
556 }
557
558 gpointer
559 _gtk_css_value_get_object (const GtkCssValue *value)
560 {
561   g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_OBJECT), NULL);
562   return value->u.ptr;
563 }
564
565 gpointer
566 _gtk_css_value_get_boxed (const GtkCssValue *value)
567 {
568   g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_BOXED), NULL);
569   return value->u.ptr;
570 }
571
572 const char **
573 _gtk_css_value_get_strv (const GtkCssValue *value)
574 {
575   g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_STRV), NULL);
576   return value->u.ptr;
577 }
578
579 GtkCssImage *
580 _gtk_css_value_get_image (const GtkCssValue *value)
581 {
582   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_IMAGE), NULL);
583   return value->u.ptr;
584 }
585
586 GtkBorderStyle
587 _gtk_css_value_get_border_style (const GtkCssValue *value)
588 {
589   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_BORDER_STYLE), 0);
590   return value->u.gint;
591 }
592
593 const GtkCssBackgroundSize *
594 _gtk_css_value_get_background_size (const GtkCssValue *value)
595 {
596   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_BACKGROUND_SIZE), NULL);
597   return value->u.ptr;
598 }
599
600 const GtkCssBackgroundPosition *
601 _gtk_css_value_get_background_position (const GtkCssValue *value)
602 {
603   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_BACKGROUND_POSITION), NULL);
604   return value->u.ptr;
605 }
606
607 const GtkCssBorderImageRepeat *
608 _gtk_css_value_get_border_image_repeat (const GtkCssValue *value)
609 {
610   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT), NULL);
611   return value->u.ptr;
612 }
613
614 const GtkCssBorderCornerRadius *
615 _gtk_css_value_get_border_corner_radius (const GtkCssValue *value)
616 {
617   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS), NULL);
618   return value->u.ptr;
619 }
620
621 PangoStyle
622 _gtk_css_value_get_pango_style (const GtkCssValue *value)
623 {
624   g_return_val_if_fail (_gtk_css_value_holds (value, PANGO_TYPE_STYLE), 0);
625   return value->u.gint;
626 }
627
628 PangoVariant
629 _gtk_css_value_get_pango_variant (const GtkCssValue *value)
630 {
631   g_return_val_if_fail (_gtk_css_value_holds (value, PANGO_TYPE_VARIANT), 0);
632   return value->u.gint;
633 }
634
635 PangoWeight
636 _gtk_css_value_get_pango_weight (const GtkCssValue *value)
637 {
638   g_return_val_if_fail (_gtk_css_value_holds (value, PANGO_TYPE_WEIGHT), 0);
639   return value->u.gint;
640 }
641
642 const GdkRGBA *
643 _gtk_css_value_get_rgba (const GtkCssValue *value)
644 {
645   g_return_val_if_fail (_gtk_css_value_holds (value, GDK_TYPE_RGBA), NULL);
646   return value->u.ptr;
647 }
648
649 cairo_pattern_t *
650 _gtk_css_value_get_pattern (const GtkCssValue *value)
651 {
652   g_return_val_if_fail (_gtk_css_value_holds (value, CAIRO_GOBJECT_TYPE_PATTERN), NULL);
653   return value->u.ptr;
654 }
655
656 GtkGradient *
657 _gtk_css_value_get_gradient (const GtkCssValue *value)
658 {
659   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_GRADIENT), NULL);
660   return value->u.ptr;
661 }
662
663 GtkShadow *
664 _gtk_css_value_get_shadow (const GtkCssValue *value)
665 {
666   g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_SHADOW), NULL);
667   return value->u.ptr;
668 }