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:
384 * @property: the property
385 * @value: an uninitialized value
386 * @parser: the parser to parse from
387 * @base: the base file for @aprser
389 * Tries to parse the given @property from the given @parser into
390 * @value. The type that @value will be assigned is dependant on
391 * the parser and no assumptions must be made about it. If the
392 * parsing fails, %FALSE will be returned and @value will be
393 * left uninitialized.
395 * Only if @property is a #GtkCssShorthandProperty, the @value will
396 * always contain a #GValueArray with the values to be used for
399 * Returns: %TRUE on success
402 _gtk_style_property_parse_value (GtkStyleProperty *property,
404 GtkCssParser *parser,
407 GtkStylePropertyClass *klass;
409 g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), FALSE);
410 g_return_val_if_fail (value != NULL, FALSE);
411 g_return_val_if_fail (parser != NULL, FALSE);
413 klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
415 return klass->parse_value (property, value, parser, base);
419 * _gtk_style_property_assign:
420 * @property: the property
421 * @props: The properties to assign to
422 * @state: The state to assign
423 * @value: (out): the #GValue with the value to be
426 * This function is called by gtk_style_properties_set() and in
427 * turn gtk_style_context_set() and similar functions to set the
428 * value from code using old APIs.
431 _gtk_style_property_assign (GtkStyleProperty *property,
432 GtkStyleProperties *props,
436 GtkStylePropertyClass *klass;
438 g_return_if_fail (GTK_IS_STYLE_PROPERTY (property));
439 g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
440 g_return_if_fail (value != NULL);
442 klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
444 klass->assign (property, props, state, value);
448 * _gtk_style_property_query:
449 * @property: the property
450 * @props: The properties to query
451 * @state: The state to query
452 * @value: (out): an uninitialized #GValue to be filled with the
453 * contents of the lookup
455 * This function is called by gtk_style_properties_get() and in
456 * turn gtk_style_context_get() and similar functions to get the
457 * value to return to code using old APIs.
460 _gtk_style_property_query (GtkStyleProperty *property,
461 GtkStyleProperties *props,
463 GtkStylePropertyContext *context,
466 GtkStylePropertyClass *klass;
468 g_return_if_fail (property != NULL);
469 g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
470 g_return_if_fail (context != NULL);
471 g_return_if_fail (value != NULL);
473 klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
475 g_value_init (value, property->value_type);
477 klass->query (property, props, state, context, value);
480 #define rgba_init(rgba, r, g, b, a) G_STMT_START{ \
482 (rgba)->green = (g); \
483 (rgba)->blue = (b); \
484 (rgba)->alpha = (a); \
487 gtk_style_property_init_properties (void)
489 static gboolean initialized = FALSE;
490 GValue value = { 0, };
491 char *default_font_family[] = { "Sans", NULL };
493 GtkCssBorderCornerRadius no_corner_radius = { 0, };
495 if (G_LIKELY (initialized))
500 g_value_init (&value, GDK_TYPE_RGBA);
501 rgba_init (&rgba, 1, 1, 1, 1);
502 g_value_set_boxed (&value, &rgba);
503 _gtk_style_property_register (g_param_spec_boxed ("color",
507 GTK_STYLE_PROPERTY_INHERIT,
512 rgba_init (&rgba, 0, 0, 0, 0);
513 g_value_set_boxed (&value, &rgba);
514 _gtk_style_property_register (g_param_spec_boxed ("background-color",
523 g_value_unset (&value);
525 g_value_init (&value, G_TYPE_STRV);
526 g_value_set_boxed (&value, default_font_family);
527 _gtk_style_property_register (g_param_spec_boxed ("font-family",
531 GTK_STYLE_PROPERTY_INHERIT,
534 font_family_value_print,
536 g_value_unset (&value);
537 _gtk_style_property_register (g_param_spec_enum ("font-style",
541 PANGO_STYLE_NORMAL, 0),
542 GTK_STYLE_PROPERTY_INHERIT,
547 _gtk_style_property_register (g_param_spec_enum ("font-variant",
551 PANGO_VARIANT_NORMAL, 0),
552 GTK_STYLE_PROPERTY_INHERIT,
557 /* xxx: need to parse this properly, ie parse the numbers */
558 _gtk_style_property_register (g_param_spec_enum ("font-weight",
562 PANGO_WEIGHT_NORMAL, 0),
563 GTK_STYLE_PROPERTY_INHERIT,
568 g_value_init (&value, G_TYPE_DOUBLE);
569 g_value_set_double (&value, 10);
570 _gtk_style_property_register (g_param_spec_double ("font-size",
573 0, G_MAXDOUBLE, 0, 0),
574 GTK_STYLE_PROPERTY_INHERIT,
579 g_value_unset (&value);
581 _gtk_style_property_register (g_param_spec_boxed ("text-shadow",
585 GTK_STYLE_PROPERTY_INHERIT,
591 _gtk_style_property_register (g_param_spec_boxed ("icon-shadow",
595 GTK_STYLE_PROPERTY_INHERIT,
601 gtk_style_properties_register_property (NULL,
602 g_param_spec_boxed ("box-shadow",
605 GTK_TYPE_SHADOW, 0));
606 gtk_style_properties_register_property (NULL,
607 g_param_spec_int ("margin-top",
611 gtk_style_properties_register_property (NULL,
612 g_param_spec_int ("margin-left",
616 gtk_style_properties_register_property (NULL,
617 g_param_spec_int ("margin-bottom",
621 gtk_style_properties_register_property (NULL,
622 g_param_spec_int ("margin-right",
626 gtk_style_properties_register_property (NULL,
627 g_param_spec_int ("padding-top",
631 gtk_style_properties_register_property (NULL,
632 g_param_spec_int ("padding-left",
636 gtk_style_properties_register_property (NULL,
637 g_param_spec_int ("padding-bottom",
641 gtk_style_properties_register_property (NULL,
642 g_param_spec_int ("padding-right",
646 gtk_style_properties_register_property (NULL,
647 g_param_spec_int ("border-top-width",
649 "Border width at top",
651 gtk_style_properties_register_property (NULL,
652 g_param_spec_int ("border-left-width",
654 "Border width at left",
656 gtk_style_properties_register_property (NULL,
657 g_param_spec_int ("border-bottom-width",
658 "border bottom width",
659 "Border width at bottom",
661 gtk_style_properties_register_property (NULL,
662 g_param_spec_int ("border-right-width",
663 "border right width",
664 "Border width at right",
667 g_value_init (&value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
668 g_value_set_boxed (&value, &no_corner_radius);
669 _gtk_style_property_register (g_param_spec_boxed ("border-top-left-radius",
670 "Border top left radius",
671 "Border radius of top left corner, in pixels",
672 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
675 border_corner_radius_value_parse,
676 border_corner_radius_value_print,
678 _gtk_style_property_register (g_param_spec_boxed ("border-top-right-radius",
679 "Border top right radius",
680 "Border radius of top right corner, in pixels",
681 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
684 border_corner_radius_value_parse,
685 border_corner_radius_value_print,
687 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-right-radius",
688 "Border bottom right radius",
689 "Border radius of bottom right corner, in pixels",
690 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
693 border_corner_radius_value_parse,
694 border_corner_radius_value_print,
696 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-left-radius",
697 "Border bottom left radius",
698 "Border radius of bottom left corner, in pixels",
699 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
702 border_corner_radius_value_parse,
703 border_corner_radius_value_print,
705 g_value_unset (&value);
707 gtk_style_properties_register_property (NULL,
708 g_param_spec_enum ("border-style",
711 GTK_TYPE_BORDER_STYLE,
712 GTK_BORDER_STYLE_NONE, 0));
713 gtk_style_properties_register_property (NULL,
714 g_param_spec_enum ("background-clip",
718 GTK_CSS_AREA_BORDER_BOX, 0));
719 gtk_style_properties_register_property (NULL,
720 g_param_spec_enum ("background-origin",
724 GTK_CSS_AREA_PADDING_BOX, 0));
725 g_value_init (&value, GTK_TYPE_CSS_SPECIAL_VALUE);
726 g_value_set_enum (&value, GTK_CSS_CURRENT_COLOR);
727 _gtk_style_property_register (g_param_spec_boxed ("border-top-color",
736 _gtk_style_property_register (g_param_spec_boxed ("border-right-color",
737 "Border right color",
738 "Border right color",
745 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-color",
746 "Border bottom color",
747 "Border bottom color",
754 _gtk_style_property_register (g_param_spec_boxed ("border-left-color",
763 g_value_unset (&value);
765 gtk_style_properties_register_property (NULL,
766 g_param_spec_boxed ("background-image",
769 CAIRO_GOBJECT_TYPE_PATTERN, 0));
770 gtk_style_properties_register_property (NULL,
771 g_param_spec_boxed ("background-repeat",
774 GTK_TYPE_CSS_BACKGROUND_REPEAT, 0));
776 gtk_style_properties_register_property (NULL,
777 g_param_spec_boxed ("border-image-source",
778 "Border image source",
779 "Border image source",
780 CAIRO_GOBJECT_TYPE_PATTERN, 0));
781 gtk_style_properties_register_property (NULL,
782 g_param_spec_boxed ("border-image-repeat",
783 "Border image repeat",
784 "Border image repeat",
785 GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
786 gtk_style_properties_register_property (NULL,
787 g_param_spec_boxed ("border-image-slice",
788 "Border image slice",
789 "Border image slice",
790 GTK_TYPE_BORDER, 0));
791 g_value_init (&value, GTK_TYPE_BORDER);
792 _gtk_style_property_register (g_param_spec_boxed ("border-image-width",
793 "Border image width",
794 "Border image width",
801 g_value_unset (&value);
802 gtk_style_properties_register_property (NULL,
803 g_param_spec_object ("engine",
806 GTK_TYPE_THEMING_ENGINE, 0));
807 gtk_style_properties_register_property (NULL,
808 g_param_spec_boxed ("transition",
809 "Transition animation description",
810 "Transition animation description",
811 GTK_TYPE_ANIMATION_DESCRIPTION, 0));
813 /* Private property holding the binding sets */
814 _gtk_style_property_register (g_param_spec_boxed ("gtk-key-bindings",
817 G_TYPE_PTR_ARRAY, 0),
820 bindings_value_parse,
821 bindings_value_print,
824 /* initialize shorthands last, they depend on the real properties existing */
825 _gtk_css_shorthand_property_init_properties ();
829 * _gtk_style_property_lookup:
830 * @name: name of the property to lookup
832 * Looks up the CSS property with the given @name. If no such
833 * property exists, %NULL is returned.
835 * Returns: (transfer none): The property or %NULL if no
836 * property with the given name exists.
839 _gtk_style_property_lookup (const char *name)
841 GtkStylePropertyClass *klass;
843 g_return_val_if_fail (name != NULL, NULL);
845 gtk_style_property_init_properties ();
847 klass = g_type_class_peek (GTK_TYPE_STYLE_PROPERTY);
849 return g_hash_table_lookup (klass->properties, name);
853 * _gtk_style_property_get_name:
854 * @property: the property to query
856 * Gets the name of the given property.
858 * Returns: the name of the property
861 _gtk_style_property_get_name (GtkStyleProperty *property)
863 g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), NULL);
865 return property->name;
869 * _gtk_style_property_get_value_type:
870 * @property: the property to query
872 * Gets the value type of the @property, if the property is usable
873 * in public API via _gtk_style_property_assign() and
874 * _gtk_style_property_query(). If the @property is not usable in that
875 * way, %G_TYPE_NONE is returned.
877 * Returns: the value type in use or %G_TYPE_NONE if none.
880 _gtk_style_property_get_value_type (GtkStyleProperty *property)
882 g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), G_TYPE_NONE);
884 return property->value_type;
889 _gtk_style_property_register (GParamSpec *pspec,
890 GtkStylePropertyFlags flags,
891 GtkStylePropertyParser property_parse_func,
892 GtkStyleParseFunc parse_func,
893 GtkStylePrintFunc print_func,
894 const GValue * initial_value)
896 GtkStyleProperty *node;
897 GValue initial_fallback = { 0, };
899 if (initial_value == NULL)
901 g_value_init (&initial_fallback, pspec->value_type);
902 if (pspec->value_type == GTK_TYPE_THEMING_ENGINE)
903 g_value_set_object (&initial_fallback, gtk_theming_engine_load (NULL));
904 else if (pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
905 g_value_take_boxed (&initial_fallback, pango_font_description_from_string ("Sans 10"));
906 else if (pspec->value_type == GDK_TYPE_RGBA)
909 gdk_rgba_parse (&color, "pink");
910 g_value_set_boxed (&initial_fallback, &color);
912 else if (pspec->value_type == GTK_TYPE_BORDER)
914 g_value_take_boxed (&initial_fallback, gtk_border_new ());
917 g_param_value_set_default (pspec, &initial_fallback);
919 initial_value = &initial_fallback;
922 node = g_object_new (GTK_TYPE_CSS_STYLE_PROPERTY,
923 "inherit", (flags & GTK_STYLE_PROPERTY_INHERIT) ? TRUE : FALSE,
924 "initial-value", initial_value,
926 "value-type", pspec->value_type,
928 g_assert (node->value_type == pspec->value_type);
929 GTK_CSS_STYLE_PROPERTY (node)->pspec = pspec;
930 node->property_parse_func = property_parse_func;
931 node->parse_func = parse_func;
932 node->print_func = print_func;
934 if (G_IS_VALUE (&initial_fallback))
935 g_value_unset (&initial_fallback);