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 g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), FALSE);
389 g_return_val_if_fail (value != NULL, FALSE);
390 g_return_val_if_fail (parser != NULL, FALSE);
392 if (_gtk_css_parser_try (parser, "initial", TRUE))
394 /* the initial value can be explicitly specified with the
395 * ‘initial’ keyword which all properties accept.
397 g_value_unset (value);
398 g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE);
399 g_value_set_enum (value, GTK_CSS_INITIAL);
402 else if (_gtk_css_parser_try (parser, "inherit", TRUE))
404 /* All properties accept the ‘inherit’ value which
405 * explicitly specifies that the value will be determined
406 * by inheritance. The ‘inherit’ value can be used to
407 * strengthen inherited values in the cascade, and it can
408 * also be used on properties that are not normally inherited.
410 g_value_unset (value);
411 g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE);
412 g_value_set_enum (value, GTK_CSS_INHERIT);
415 else if (property->property_parse_func)
417 GError *error = NULL;
421 value_str = _gtk_css_parser_read_value (parser);
422 if (value_str == NULL)
425 success = (*property->property_parse_func) (value_str, value, &error);
431 else if (property->parse_func)
432 return (* property->parse_func) (parser, base, value);
434 return _gtk_css_style_parse_value (value, parser, base);
438 _gtk_style_property_unpack (GtkStyleProperty *property,
442 g_return_val_if_fail (property != NULL, NULL);
443 g_return_val_if_fail (property->unpack_func != NULL, NULL);
444 g_return_val_if_fail (value != NULL, NULL);
445 g_return_val_if_fail (n_params != NULL, NULL);
447 return property->unpack_func (value, n_params);
451 * _gtk_style_property_assign:
452 * @property: the property
453 * @props: The properties to assign to
454 * @state: The state to assign
455 * @value: (out): the #GValue with the value to be
458 * This function is called by gtk_style_properties_set() and in
459 * turn gtk_style_context_set() and similar functions to set the
460 * value from code using old APIs.
463 _gtk_style_property_assign (GtkStyleProperty *property,
464 GtkStyleProperties *props,
468 GtkStylePropertyClass *klass;
470 g_return_if_fail (GTK_IS_STYLE_PROPERTY (property));
471 g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
472 g_return_if_fail (value != NULL);
474 klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
476 klass->assign (property, props, state, value);
480 * _gtk_style_property_query:
481 * @property: the property
482 * @props: The properties to query
483 * @state: The state to query
484 * @value: (out): an uninitialized #GValue to be filled with the
485 * contents of the lookup
487 * This function is called by gtk_style_properties_get() and in
488 * turn gtk_style_context_get() and similar functions to get the
489 * value to return to code using old APIs.
492 _gtk_style_property_query (GtkStyleProperty *property,
493 GtkStyleProperties *props,
495 GtkStylePropertyContext *context,
498 GtkStylePropertyClass *klass;
500 g_return_if_fail (property != NULL);
501 g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
502 g_return_if_fail (context != NULL);
503 g_return_if_fail (value != NULL);
505 klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
507 g_value_init (value, property->value_type);
509 klass->query (property, props, state, context, value);
512 #define rgba_init(rgba, r, g, b, a) G_STMT_START{ \
514 (rgba)->green = (g); \
515 (rgba)->blue = (b); \
516 (rgba)->alpha = (a); \
519 gtk_style_property_init_properties (void)
521 static gboolean initialized = FALSE;
522 GValue value = { 0, };
523 char *default_font_family[] = { "Sans", NULL };
526 if (G_LIKELY (initialized))
531 g_value_init (&value, GDK_TYPE_RGBA);
532 rgba_init (&rgba, 1, 1, 1, 1);
533 g_value_set_boxed (&value, &rgba);
534 _gtk_style_property_register (g_param_spec_boxed ("color",
538 GTK_STYLE_PROPERTY_INHERIT,
543 rgba_init (&rgba, 0, 0, 0, 0);
544 g_value_set_boxed (&value, &rgba);
545 _gtk_style_property_register (g_param_spec_boxed ("background-color",
554 g_value_unset (&value);
556 g_value_init (&value, G_TYPE_STRV);
557 g_value_set_boxed (&value, default_font_family);
558 _gtk_style_property_register (g_param_spec_boxed ("font-family",
562 GTK_STYLE_PROPERTY_INHERIT,
565 font_family_value_print,
567 g_value_unset (&value);
568 _gtk_style_property_register (g_param_spec_enum ("font-style",
572 PANGO_STYLE_NORMAL, 0),
573 GTK_STYLE_PROPERTY_INHERIT,
578 _gtk_style_property_register (g_param_spec_enum ("font-variant",
582 PANGO_VARIANT_NORMAL, 0),
583 GTK_STYLE_PROPERTY_INHERIT,
588 /* xxx: need to parse this properly, ie parse the numbers */
589 _gtk_style_property_register (g_param_spec_enum ("font-weight",
593 PANGO_WEIGHT_NORMAL, 0),
594 GTK_STYLE_PROPERTY_INHERIT,
599 g_value_init (&value, G_TYPE_DOUBLE);
600 g_value_set_double (&value, 10);
601 _gtk_style_property_register (g_param_spec_double ("font-size",
604 0, G_MAXDOUBLE, 0, 0),
605 GTK_STYLE_PROPERTY_INHERIT,
610 g_value_unset (&value);
612 _gtk_style_property_register (g_param_spec_boxed ("text-shadow",
616 GTK_STYLE_PROPERTY_INHERIT,
622 _gtk_style_property_register (g_param_spec_boxed ("icon-shadow",
626 GTK_STYLE_PROPERTY_INHERIT,
632 gtk_style_properties_register_property (NULL,
633 g_param_spec_boxed ("box-shadow",
636 GTK_TYPE_SHADOW, 0));
637 gtk_style_properties_register_property (NULL,
638 g_param_spec_int ("margin-top",
642 gtk_style_properties_register_property (NULL,
643 g_param_spec_int ("margin-left",
647 gtk_style_properties_register_property (NULL,
648 g_param_spec_int ("margin-bottom",
652 gtk_style_properties_register_property (NULL,
653 g_param_spec_int ("margin-right",
657 gtk_style_properties_register_property (NULL,
658 g_param_spec_int ("padding-top",
662 gtk_style_properties_register_property (NULL,
663 g_param_spec_int ("padding-left",
667 gtk_style_properties_register_property (NULL,
668 g_param_spec_int ("padding-bottom",
672 gtk_style_properties_register_property (NULL,
673 g_param_spec_int ("padding-right",
677 gtk_style_properties_register_property (NULL,
678 g_param_spec_int ("border-top-width",
680 "Border width at top",
682 gtk_style_properties_register_property (NULL,
683 g_param_spec_int ("border-left-width",
685 "Border width at left",
687 gtk_style_properties_register_property (NULL,
688 g_param_spec_int ("border-bottom-width",
689 "border bottom width",
690 "Border width at bottom",
692 gtk_style_properties_register_property (NULL,
693 g_param_spec_int ("border-right-width",
694 "border right width",
695 "Border width at right",
698 _gtk_style_property_register (g_param_spec_boxed ("border-top-left-radius",
699 "Border top left radius",
700 "Border radius of top left corner, in pixels",
701 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
704 border_corner_radius_value_parse,
705 border_corner_radius_value_print,
707 _gtk_style_property_register (g_param_spec_boxed ("border-top-right-radius",
708 "Border top right radius",
709 "Border radius of top right corner, in pixels",
710 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
713 border_corner_radius_value_parse,
714 border_corner_radius_value_print,
716 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-right-radius",
717 "Border bottom right radius",
718 "Border radius of bottom right corner, in pixels",
719 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
722 border_corner_radius_value_parse,
723 border_corner_radius_value_print,
725 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-left-radius",
726 "Border bottom left radius",
727 "Border radius of bottom left corner, in pixels",
728 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
731 border_corner_radius_value_parse,
732 border_corner_radius_value_print,
735 gtk_style_properties_register_property (NULL,
736 g_param_spec_enum ("border-style",
739 GTK_TYPE_BORDER_STYLE,
740 GTK_BORDER_STYLE_NONE, 0));
741 gtk_style_properties_register_property (NULL,
742 g_param_spec_enum ("background-clip",
746 GTK_CSS_AREA_BORDER_BOX, 0));
747 gtk_style_properties_register_property (NULL,
748 g_param_spec_enum ("background-origin",
752 GTK_CSS_AREA_PADDING_BOX, 0));
753 g_value_init (&value, GTK_TYPE_CSS_SPECIAL_VALUE);
754 g_value_set_enum (&value, GTK_CSS_CURRENT_COLOR);
755 _gtk_style_property_register (g_param_spec_boxed ("border-top-color",
764 _gtk_style_property_register (g_param_spec_boxed ("border-right-color",
765 "Border right color",
766 "Border right color",
773 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-color",
774 "Border bottom color",
775 "Border bottom color",
782 _gtk_style_property_register (g_param_spec_boxed ("border-left-color",
791 g_value_unset (&value);
793 gtk_style_properties_register_property (NULL,
794 g_param_spec_boxed ("background-image",
797 CAIRO_GOBJECT_TYPE_PATTERN, 0));
798 gtk_style_properties_register_property (NULL,
799 g_param_spec_boxed ("background-repeat",
802 GTK_TYPE_CSS_BACKGROUND_REPEAT, 0));
804 gtk_style_properties_register_property (NULL,
805 g_param_spec_boxed ("border-image-source",
806 "Border image source",
807 "Border image source",
808 CAIRO_GOBJECT_TYPE_PATTERN, 0));
809 gtk_style_properties_register_property (NULL,
810 g_param_spec_boxed ("border-image-repeat",
811 "Border image repeat",
812 "Border image repeat",
813 GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
814 gtk_style_properties_register_property (NULL,
815 g_param_spec_boxed ("border-image-slice",
816 "Border image slice",
817 "Border image slice",
818 GTK_TYPE_BORDER, 0));
819 g_value_init (&value, GTK_TYPE_BORDER);
820 _gtk_style_property_register (g_param_spec_boxed ("border-image-width",
821 "Border image width",
822 "Border image width",
829 g_value_unset (&value);
830 gtk_style_properties_register_property (NULL,
831 g_param_spec_object ("engine",
834 GTK_TYPE_THEMING_ENGINE, 0));
835 gtk_style_properties_register_property (NULL,
836 g_param_spec_boxed ("transition",
837 "Transition animation description",
838 "Transition animation description",
839 GTK_TYPE_ANIMATION_DESCRIPTION, 0));
841 /* Private property holding the binding sets */
842 _gtk_style_property_register (g_param_spec_boxed ("gtk-key-bindings",
845 G_TYPE_PTR_ARRAY, 0),
848 bindings_value_parse,
849 bindings_value_print,
852 /* initialize shorthands last, they depend on the real properties existing */
853 _gtk_css_shorthand_property_init_properties ();
857 * _gtk_style_property_lookup:
858 * @name: name of the property to lookup
860 * Looks up the CSS property with the given @name. If no such
861 * property exists, %NULL is returned.
863 * Returns: (transfer none): The property or %NULL if no
864 * property with the given name exists.
867 _gtk_style_property_lookup (const char *name)
869 GtkStylePropertyClass *klass;
871 g_return_val_if_fail (name != NULL, NULL);
873 gtk_style_property_init_properties ();
875 klass = g_type_class_peek (GTK_TYPE_STYLE_PROPERTY);
877 return g_hash_table_lookup (klass->properties, name);
881 * _gtk_style_property_get_name:
882 * @property: the property to query
884 * Gets the name of the given property.
886 * Returns: the name of the property
889 _gtk_style_property_get_name (GtkStyleProperty *property)
891 g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), NULL);
893 return property->name;
897 * _gtk_style_property_get_value_type:
898 * @property: the property to query
900 * Gets the value type of the @property, if the property is usable
901 * in public API via _gtk_style_property_assign() and
902 * _gtk_style_property_query(). If the @property is not usable in that
903 * way, %G_TYPE_NONE is returned.
905 * Returns: the value type in use or %G_TYPE_NONE if none.
908 _gtk_style_property_get_value_type (GtkStyleProperty *property)
910 g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), G_TYPE_NONE);
912 return property->value_type;
917 _gtk_style_property_register (GParamSpec *pspec,
918 GtkStylePropertyFlags flags,
919 GtkStylePropertyParser property_parse_func,
920 GtkStyleParseFunc parse_func,
921 GtkStylePrintFunc print_func,
922 const GValue * initial_value)
924 GtkStyleProperty *node;
925 GValue initial_fallback = { 0, };
927 if (initial_value == NULL)
929 g_value_init (&initial_fallback, pspec->value_type);
930 if (pspec->value_type == GTK_TYPE_THEMING_ENGINE)
931 g_value_set_object (&initial_fallback, gtk_theming_engine_load (NULL));
932 else if (pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
933 g_value_take_boxed (&initial_fallback, pango_font_description_from_string ("Sans 10"));
934 else if (pspec->value_type == GDK_TYPE_RGBA)
937 gdk_rgba_parse (&color, "pink");
938 g_value_set_boxed (&initial_fallback, &color);
940 else if (pspec->value_type == GTK_TYPE_BORDER)
942 g_value_take_boxed (&initial_fallback, gtk_border_new ());
945 g_param_value_set_default (pspec, &initial_fallback);
947 initial_value = &initial_fallback;
950 node = g_object_new (GTK_TYPE_CSS_STYLE_PROPERTY,
951 "inherit", (flags & GTK_STYLE_PROPERTY_INHERIT) ? TRUE : FALSE,
952 "initial-value", initial_value,
954 "value-type", pspec->value_type,
956 g_assert (node->value_type == pspec->value_type);
958 node->property_parse_func = property_parse_func;
959 node->parse_func = parse_func;
960 node->print_func = print_func;
962 if (G_IS_VALUE (&initial_fallback))
963 g_value_unset (&initial_fallback);