1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
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.
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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
22 #include "gtkstylepropertyprivate.h"
29 #include <gdk-pixbuf/gdk-pixbuf.h>
30 #include <cairo-gobject.h>
32 #include "gtkcssprovider.h"
33 #include "gtkcssparserprivate.h"
34 #include "gtkcssshorthandpropertyprivate.h"
35 #include "gtkcssstylefuncsprivate.h"
36 #include "gtkcssstylepropertyprivate.h"
37 #include "gtkcsstypesprivate.h"
39 #include "gtkprivatetypebuiltins.h"
40 #include "gtkstylepropertiesprivate.h"
42 /* the actual parsers we have */
43 #include "gtkanimationdescription.h"
44 #include "gtkbindings.h"
45 #include "gtkgradient.h"
46 #include "gtkshadowprivate.h"
47 #include "gtkthemingengine.h"
48 #include "gtktypebuiltins.h"
49 #include "gtkwin32themeprivate.h"
51 /* this is in case round() is not provided by the compiler,
52 * such as in the case of C89 compilers, like MSVC
54 #include "fallback-c89.c"
62 G_DEFINE_ABSTRACT_TYPE (GtkStyleProperty, _gtk_style_property, G_TYPE_OBJECT)
65 gtk_style_property_finalize (GObject *object)
67 GtkStyleProperty *property = GTK_STYLE_PROPERTY (object);
69 g_warning ("finalizing %s `%s', how could this happen?", G_OBJECT_TYPE_NAME (object), property->name);
71 G_OBJECT_CLASS (_gtk_style_property_parent_class)->finalize (object);
75 gtk_style_property_set_property (GObject *object,
80 GtkStyleProperty *property = GTK_STYLE_PROPERTY (object);
81 GtkStylePropertyClass *klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
86 property->name = g_value_dup_string (value);
87 g_assert (property->name);
88 g_assert (g_hash_table_lookup (klass->properties, property->name) == NULL);
89 g_hash_table_insert (klass->properties, property->name, property);
92 property->value_type = g_value_get_gtype (value);
95 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
101 gtk_style_property_get_property (GObject *object,
106 GtkStyleProperty *property = GTK_STYLE_PROPERTY (object);
111 g_value_set_string (value, property->name);
113 case PROP_VALUE_TYPE:
114 g_value_set_gtype (value, property->value_type);
117 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
123 _gtk_style_property_class_init (GtkStylePropertyClass *klass)
125 GObjectClass *object_class = G_OBJECT_CLASS (klass);
127 object_class->finalize = gtk_style_property_finalize;
128 object_class->set_property = gtk_style_property_set_property;
129 object_class->get_property = gtk_style_property_get_property;
131 g_object_class_install_property (object_class,
133 g_param_spec_string ("name",
135 P_("The name of the property"),
137 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
138 g_object_class_install_property (object_class,
140 g_param_spec_gtype ("value-type",
142 P_("The value type returned by GtkStyleContext"),
144 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
146 klass->properties = g_hash_table_new (g_str_hash, g_str_equal);
150 _gtk_style_property_init (GtkStyleProperty *property)
152 property->value_type = G_TYPE_NONE;
156 string_append_double (GString *string,
159 char buf[G_ASCII_DTOSTR_BUF_SIZE];
161 g_ascii_dtostr (buf, sizeof (buf), d);
162 g_string_append (string, buf);
166 string_append_string (GString *str,
171 g_string_append_c (str, '"');
174 len = strcspn (string, "\"\n\r\f");
175 g_string_append (str, string);
182 g_string_append (str, "\\A ");
185 g_string_append (str, "\\D ");
188 g_string_append (str, "\\C ");
191 g_string_append (str, "\\\"");
194 g_assert_not_reached ();
199 g_string_append_c (str, '"');
202 /*** IMPLEMENTATIONS ***/
205 font_family_parse (GtkCssParser *parser,
212 /* We don't special case generic families. Pango should do
215 names = g_ptr_array_new ();
218 name = _gtk_css_parser_try_ident (parser, TRUE);
221 GString *string = g_string_new (name);
223 while ((name = _gtk_css_parser_try_ident (parser, TRUE)))
225 g_string_append_c (string, ' ');
226 g_string_append (string, name);
229 name = g_string_free (string, FALSE);
233 name = _gtk_css_parser_read_string (parser);
236 g_ptr_array_free (names, TRUE);
241 g_ptr_array_add (names, name);
242 } while (_gtk_css_parser_try (parser, ",", TRUE));
244 /* NULL-terminate array */
245 g_ptr_array_add (names, NULL);
246 g_value_set_boxed (value, g_ptr_array_free (names, FALSE));
251 font_family_value_print (const GValue *value,
254 const char **names = g_value_get_boxed (value);
256 if (names == NULL || *names == NULL)
258 g_string_append (string, "none");
262 string_append_string (string, *names);
266 g_string_append (string, ", ");
267 string_append_string (string, *names);
273 bindings_value_parse (GtkCssParser *parser,
278 GtkBindingSet *binding_set;
281 array = g_ptr_array_new ();
284 name = _gtk_css_parser_try_ident (parser, TRUE);
287 _gtk_css_parser_error (parser, "Not a valid binding name");
288 g_ptr_array_free (array, TRUE);
292 binding_set = gtk_binding_set_find (name);
296 _gtk_css_parser_error (parser, "No binding set named '%s'", name);
301 g_ptr_array_add (array, binding_set);
304 while (_gtk_css_parser_try (parser, ",", TRUE));
306 g_value_take_boxed (value, array);
312 bindings_value_print (const GValue *value,
318 array = g_value_get_boxed (value);
320 for (i = 0; i < array->len; i++)
322 GtkBindingSet *binding_set = g_ptr_array_index (array, i);
325 g_string_append (string, ", ");
326 g_string_append (string, binding_set->set_name);
331 border_corner_radius_value_parse (GtkCssParser *parser,
335 GtkCssBorderCornerRadius corner;
337 if (!_gtk_css_parser_try_double (parser, &corner.horizontal))
339 _gtk_css_parser_error (parser, "Expected a number");
342 else if (corner.horizontal < 0)
345 if (!_gtk_css_parser_try_double (parser, &corner.vertical))
346 corner.vertical = corner.horizontal;
347 else if (corner.vertical < 0)
350 g_value_set_boxed (value, &corner);
354 _gtk_css_parser_error (parser, "Border radius values cannot be negative");
359 border_corner_radius_value_print (const GValue *value,
362 GtkCssBorderCornerRadius *corner;
364 corner = g_value_get_boxed (value);
368 g_string_append (string, "none");
372 string_append_double (string, corner->horizontal);
373 if (corner->horizontal != corner->vertical)
375 g_string_append_c (string, ' ');
376 string_append_double (string, corner->vertical);
383 _gtk_style_property_parse_value (GtkStyleProperty *property,
385 GtkCssParser *parser,
388 GtkStylePropertyClass *klass;
390 g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), FALSE);
391 g_return_val_if_fail (value != NULL, FALSE);
392 g_return_val_if_fail (parser != NULL, FALSE);
394 klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
396 return klass->parse_value (property, value, parser, base);
400 * _gtk_style_property_assign:
401 * @property: the property
402 * @props: The properties to assign to
403 * @state: The state to assign
404 * @value: (out): the #GValue with the value to be
407 * This function is called by gtk_style_properties_set() and in
408 * turn gtk_style_context_set() and similar functions to set the
409 * value from code using old APIs.
412 _gtk_style_property_assign (GtkStyleProperty *property,
413 GtkStyleProperties *props,
417 GtkStylePropertyClass *klass;
419 g_return_if_fail (GTK_IS_STYLE_PROPERTY (property));
420 g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
421 g_return_if_fail (value != NULL);
423 klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
425 klass->assign (property, props, state, value);
429 * _gtk_style_property_query:
430 * @property: the property
431 * @props: The properties to query
432 * @state: The state to query
433 * @value: (out): an uninitialized #GValue to be filled with the
434 * contents of the lookup
436 * This function is called by gtk_style_properties_get() and in
437 * turn gtk_style_context_get() and similar functions to get the
438 * value to return to code using old APIs.
441 _gtk_style_property_query (GtkStyleProperty *property,
442 GtkStyleProperties *props,
444 GtkStylePropertyContext *context,
447 GtkStylePropertyClass *klass;
449 g_return_if_fail (property != NULL);
450 g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
451 g_return_if_fail (context != NULL);
452 g_return_if_fail (value != NULL);
454 klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
456 g_value_init (value, property->value_type);
458 klass->query (property, props, state, context, value);
461 #define rgba_init(rgba, r, g, b, a) G_STMT_START{ \
463 (rgba)->green = (g); \
464 (rgba)->blue = (b); \
465 (rgba)->alpha = (a); \
468 gtk_style_property_init_properties (void)
470 static gboolean initialized = FALSE;
471 GValue value = { 0, };
472 char *default_font_family[] = { "Sans", NULL };
474 GtkCssBorderCornerRadius no_corner_radius = { 0, };
476 if (G_LIKELY (initialized))
481 g_value_init (&value, GDK_TYPE_RGBA);
482 rgba_init (&rgba, 1, 1, 1, 1);
483 g_value_set_boxed (&value, &rgba);
484 _gtk_style_property_register (g_param_spec_boxed ("color",
488 GTK_STYLE_PROPERTY_INHERIT,
493 rgba_init (&rgba, 0, 0, 0, 0);
494 g_value_set_boxed (&value, &rgba);
495 _gtk_style_property_register (g_param_spec_boxed ("background-color",
504 g_value_unset (&value);
506 g_value_init (&value, G_TYPE_STRV);
507 g_value_set_boxed (&value, default_font_family);
508 _gtk_style_property_register (g_param_spec_boxed ("font-family",
512 GTK_STYLE_PROPERTY_INHERIT,
515 font_family_value_print,
517 g_value_unset (&value);
518 _gtk_style_property_register (g_param_spec_enum ("font-style",
522 PANGO_STYLE_NORMAL, 0),
523 GTK_STYLE_PROPERTY_INHERIT,
528 _gtk_style_property_register (g_param_spec_enum ("font-variant",
532 PANGO_VARIANT_NORMAL, 0),
533 GTK_STYLE_PROPERTY_INHERIT,
538 /* xxx: need to parse this properly, ie parse the numbers */
539 _gtk_style_property_register (g_param_spec_enum ("font-weight",
543 PANGO_WEIGHT_NORMAL, 0),
544 GTK_STYLE_PROPERTY_INHERIT,
549 g_value_init (&value, G_TYPE_DOUBLE);
550 g_value_set_double (&value, 10);
551 _gtk_style_property_register (g_param_spec_double ("font-size",
554 0, G_MAXDOUBLE, 0, 0),
555 GTK_STYLE_PROPERTY_INHERIT,
560 g_value_unset (&value);
562 _gtk_style_property_register (g_param_spec_boxed ("text-shadow",
566 GTK_STYLE_PROPERTY_INHERIT,
572 _gtk_style_property_register (g_param_spec_boxed ("icon-shadow",
576 GTK_STYLE_PROPERTY_INHERIT,
582 gtk_style_properties_register_property (NULL,
583 g_param_spec_boxed ("box-shadow",
586 GTK_TYPE_SHADOW, 0));
587 gtk_style_properties_register_property (NULL,
588 g_param_spec_int ("margin-top",
592 gtk_style_properties_register_property (NULL,
593 g_param_spec_int ("margin-left",
597 gtk_style_properties_register_property (NULL,
598 g_param_spec_int ("margin-bottom",
602 gtk_style_properties_register_property (NULL,
603 g_param_spec_int ("margin-right",
607 gtk_style_properties_register_property (NULL,
608 g_param_spec_int ("padding-top",
612 gtk_style_properties_register_property (NULL,
613 g_param_spec_int ("padding-left",
617 gtk_style_properties_register_property (NULL,
618 g_param_spec_int ("padding-bottom",
622 gtk_style_properties_register_property (NULL,
623 g_param_spec_int ("padding-right",
627 gtk_style_properties_register_property (NULL,
628 g_param_spec_int ("border-top-width",
630 "Border width at top",
632 gtk_style_properties_register_property (NULL,
633 g_param_spec_int ("border-left-width",
635 "Border width at left",
637 gtk_style_properties_register_property (NULL,
638 g_param_spec_int ("border-bottom-width",
639 "border bottom width",
640 "Border width at bottom",
642 gtk_style_properties_register_property (NULL,
643 g_param_spec_int ("border-right-width",
644 "border right width",
645 "Border width at right",
648 g_value_init (&value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
649 g_value_set_boxed (&value, &no_corner_radius);
650 _gtk_style_property_register (g_param_spec_boxed ("border-top-left-radius",
651 "Border top left radius",
652 "Border radius of top left corner, in pixels",
653 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
656 border_corner_radius_value_parse,
657 border_corner_radius_value_print,
659 _gtk_style_property_register (g_param_spec_boxed ("border-top-right-radius",
660 "Border top right radius",
661 "Border radius of top right corner, in pixels",
662 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
665 border_corner_radius_value_parse,
666 border_corner_radius_value_print,
668 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-right-radius",
669 "Border bottom right radius",
670 "Border radius of bottom right corner, in pixels",
671 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
674 border_corner_radius_value_parse,
675 border_corner_radius_value_print,
677 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-left-radius",
678 "Border bottom left radius",
679 "Border radius of bottom left corner, in pixels",
680 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
683 border_corner_radius_value_parse,
684 border_corner_radius_value_print,
686 g_value_unset (&value);
688 gtk_style_properties_register_property (NULL,
689 g_param_spec_enum ("border-style",
692 GTK_TYPE_BORDER_STYLE,
693 GTK_BORDER_STYLE_NONE, 0));
694 gtk_style_properties_register_property (NULL,
695 g_param_spec_enum ("background-clip",
699 GTK_CSS_AREA_BORDER_BOX, 0));
700 gtk_style_properties_register_property (NULL,
701 g_param_spec_enum ("background-origin",
705 GTK_CSS_AREA_PADDING_BOX, 0));
706 g_value_init (&value, GTK_TYPE_CSS_SPECIAL_VALUE);
707 g_value_set_enum (&value, GTK_CSS_CURRENT_COLOR);
708 _gtk_style_property_register (g_param_spec_boxed ("border-top-color",
717 _gtk_style_property_register (g_param_spec_boxed ("border-right-color",
718 "Border right color",
719 "Border right color",
726 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-color",
727 "Border bottom color",
728 "Border bottom color",
735 _gtk_style_property_register (g_param_spec_boxed ("border-left-color",
744 g_value_unset (&value);
746 gtk_style_properties_register_property (NULL,
747 g_param_spec_boxed ("background-image",
750 CAIRO_GOBJECT_TYPE_PATTERN, 0));
751 gtk_style_properties_register_property (NULL,
752 g_param_spec_boxed ("background-repeat",
755 GTK_TYPE_CSS_BACKGROUND_REPEAT, 0));
757 gtk_style_properties_register_property (NULL,
758 g_param_spec_boxed ("border-image-source",
759 "Border image source",
760 "Border image source",
761 CAIRO_GOBJECT_TYPE_PATTERN, 0));
762 gtk_style_properties_register_property (NULL,
763 g_param_spec_boxed ("border-image-repeat",
764 "Border image repeat",
765 "Border image repeat",
766 GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
767 gtk_style_properties_register_property (NULL,
768 g_param_spec_boxed ("border-image-slice",
769 "Border image slice",
770 "Border image slice",
771 GTK_TYPE_BORDER, 0));
772 g_value_init (&value, GTK_TYPE_BORDER);
773 _gtk_style_property_register (g_param_spec_boxed ("border-image-width",
774 "Border image width",
775 "Border image width",
782 g_value_unset (&value);
783 gtk_style_properties_register_property (NULL,
784 g_param_spec_object ("engine",
787 GTK_TYPE_THEMING_ENGINE, 0));
788 gtk_style_properties_register_property (NULL,
789 g_param_spec_boxed ("transition",
790 "Transition animation description",
791 "Transition animation description",
792 GTK_TYPE_ANIMATION_DESCRIPTION, 0));
794 /* Private property holding the binding sets */
795 _gtk_style_property_register (g_param_spec_boxed ("gtk-key-bindings",
798 G_TYPE_PTR_ARRAY, 0),
801 bindings_value_parse,
802 bindings_value_print,
805 /* initialize shorthands last, they depend on the real properties existing */
806 _gtk_css_shorthand_property_init_properties ();
810 * _gtk_style_property_lookup:
811 * @name: name of the property to lookup
813 * Looks up the CSS property with the given @name. If no such
814 * property exists, %NULL is returned.
816 * Returns: (transfer none): The property or %NULL if no
817 * property with the given name exists.
820 _gtk_style_property_lookup (const char *name)
822 GtkStylePropertyClass *klass;
824 g_return_val_if_fail (name != NULL, NULL);
826 gtk_style_property_init_properties ();
828 klass = g_type_class_peek (GTK_TYPE_STYLE_PROPERTY);
830 return g_hash_table_lookup (klass->properties, name);
834 * _gtk_style_property_get_name:
835 * @property: the property to query
837 * Gets the name of the given property.
839 * Returns: the name of the property
842 _gtk_style_property_get_name (GtkStyleProperty *property)
844 g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), NULL);
846 return property->name;
850 * _gtk_style_property_get_value_type:
851 * @property: the property to query
853 * Gets the value type of the @property, if the property is usable
854 * in public API via _gtk_style_property_assign() and
855 * _gtk_style_property_query(). If the @property is not usable in that
856 * way, %G_TYPE_NONE is returned.
858 * Returns: the value type in use or %G_TYPE_NONE if none.
861 _gtk_style_property_get_value_type (GtkStyleProperty *property)
863 g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), G_TYPE_NONE);
865 return property->value_type;
870 _gtk_style_property_register (GParamSpec *pspec,
871 GtkStylePropertyFlags flags,
872 GtkStylePropertyParser property_parse_func,
873 GtkStyleParseFunc parse_func,
874 GtkStylePrintFunc print_func,
875 const GValue * initial_value)
877 GtkStyleProperty *node;
878 GValue initial_fallback = { 0, };
880 if (initial_value == NULL)
882 g_value_init (&initial_fallback, pspec->value_type);
883 if (pspec->value_type == GTK_TYPE_THEMING_ENGINE)
884 g_value_set_object (&initial_fallback, gtk_theming_engine_load (NULL));
885 else if (pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
886 g_value_take_boxed (&initial_fallback, pango_font_description_from_string ("Sans 10"));
887 else if (pspec->value_type == GDK_TYPE_RGBA)
890 gdk_rgba_parse (&color, "pink");
891 g_value_set_boxed (&initial_fallback, &color);
893 else if (pspec->value_type == GTK_TYPE_BORDER)
895 g_value_take_boxed (&initial_fallback, gtk_border_new ());
898 g_param_value_set_default (pspec, &initial_fallback);
900 initial_value = &initial_fallback;
903 node = g_object_new (GTK_TYPE_CSS_STYLE_PROPERTY,
904 "inherit", (flags & GTK_STYLE_PROPERTY_INHERIT) ? TRUE : FALSE,
905 "initial-value", initial_value,
907 "value-type", pspec->value_type,
909 g_assert (node->value_type == pspec->value_type);
910 GTK_CSS_STYLE_PROPERTY (node)->pspec = pspec;
911 node->property_parse_func = property_parse_func;
912 node->parse_func = parse_func;
913 node->print_func = print_func;
915 if (G_IS_VALUE (&initial_fallback))
916 g_value_unset (&initial_fallback);