4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
7 * Copyright (C) 2009 Intel Corportation
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
24 * Emmanuele Bassi <ebassi@linux.intel.com>
37 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
38 #include "deprecated/clutter-container.h"
39 #include "deprecated/clutter-alpha.h"
41 #include "clutter-actor.h"
42 #include "clutter-debug.h"
43 #include "clutter-enum-types.h"
45 #include "clutter-script.h"
46 #include "clutter-script-private.h"
47 #include "clutter-scriptable.h"
49 #include "clutter-stage-manager.h"
51 #include "clutter-private.h"
53 static void clutter_script_parser_object_end (JsonParser *parser,
55 static void clutter_script_parser_parse_end (JsonParser *parser);
57 #define clutter_script_parser_get_type _clutter_script_parser_get_type
59 G_DEFINE_TYPE (ClutterScriptParser, clutter_script_parser, JSON_TYPE_PARSER);
62 clutter_script_parser_class_init (ClutterScriptParserClass *klass)
64 JsonParserClass *parser_class = JSON_PARSER_CLASS (klass);
66 parser_class->object_end = clutter_script_parser_object_end;
67 parser_class->parse_end = clutter_script_parser_parse_end;
71 clutter_script_parser_init (ClutterScriptParser *parser)
76 _clutter_script_get_type_from_symbol (const gchar *symbol)
78 static GModule *module = NULL;
80 GType gtype = G_TYPE_INVALID;
83 module = g_module_open (NULL, 0);
85 if (g_module_symbol (module, symbol, (gpointer)&func))
92 _clutter_script_get_type_from_class (const gchar *name)
94 static GModule *module = NULL;
95 GString *symbol_name = g_string_sized_new (64);
96 GType gtype = G_TYPE_INVALID;
101 if (G_UNLIKELY (!module))
102 module = g_module_open (NULL, 0);
104 for (i = 0; name[i] != '\0'; i++)
108 /* the standard naming policy for GObject-based libraries
111 * NAME := INITIAL_WORD WORD+
112 * INITIAL_WORD := [A-Z][a-z0-9]*
113 * WORD := [A-Z]{1,2}[a-z0-9]+ | [A-Z]{2,}
117 * GString -> g_string
118 * GtkCTree -> gtk_ctree
119 * ClutterX11TexturePixmap -> clutter_x11_texture_pixmap
123 * http://mail.gnome.org/archives/gtk-devel-list/2007-June/msg00022.html
127 * http://git.gnome.org/cgit/gtk+/plain/gtk/gtkbuilderparser.c
130 if ((c == g_ascii_toupper (c) &&
131 i > 0 && name[i - 1] != g_ascii_toupper (name[i - 1])) ||
132 (i > 2 && name[i] == g_ascii_toupper (name[i]) &&
133 name[i - 1] == g_ascii_toupper (name[i - 1]) &&
134 name[i - 2] == g_ascii_toupper (name[i - 2])))
135 g_string_append_c (symbol_name, '_');
137 g_string_append_c (symbol_name, g_ascii_tolower (c));
140 g_string_append (symbol_name, "_get_type");
142 symbol = g_string_free (symbol_name, FALSE);
144 if (g_module_symbol (module, symbol, (gpointer)&func))
146 CLUTTER_NOTE (SCRIPT, "Type function: %s", symbol);
156 * clutter_script_enum_from_string:
157 * @type: a #GType for an enumeration type
158 * @string: the enumeration value as a string
159 * @enum_value: (out): return location for the enumeration value as an integer
161 * Converts an enumeration value inside @string into a numeric
162 * value and places it into @enum_value.
164 * The enumeration value can be an integer, the enumeration nick
165 * or the enumeration name, as part of the #GEnumValue structure.
167 * Return value: %TRUE if the conversion was successfull.
170 _clutter_script_enum_from_string (GType type,
178 gboolean retval = TRUE;
180 g_return_val_if_fail (G_TYPE_IS_ENUM (type), 0);
181 g_return_val_if_fail (string != NULL, 0);
183 value = strtoul (string, &endptr, 0);
184 if (endptr != string) /* parsed a number */
188 eclass = g_type_class_ref (type);
189 ev = g_enum_get_value_by_name (eclass, string);
191 ev = g_enum_get_value_by_nick (eclass, string);
194 *enum_value = ev->value;
198 g_type_class_unref (eclass);
205 _clutter_script_flags_from_string (GType type,
209 gchar *endptr, *prevptr;
210 guint i, j, ret, value;
215 g_return_val_if_fail (G_TYPE_IS_FLAGS (type), 0);
216 g_return_val_if_fail (string != NULL, 0);
220 value = strtoul (string, &endptr, 0);
221 if (endptr != string) /* parsed a number */
222 *flags_value = value;
227 fclass = g_type_class_ref (type);
229 flagstr = g_strdup (string);
230 for (value = i = j = 0; ; i++)
232 gboolean eos = (flagstr[i] == '\0') ? TRUE : FALSE;
234 if (!eos && flagstr[i] != '|')
238 endptr = &flagstr[i];
249 gunichar ch = g_utf8_get_char (flag);
250 if (!g_unichar_isspace (ch))
253 flag = g_utf8_next_char (flag);
256 while (endptr > flag)
260 prevptr = g_utf8_prev_char (endptr);
262 ch = g_utf8_get_char (prevptr);
263 if (!g_unichar_isspace (ch))
273 fv = g_flags_get_value_by_name (fclass, flag);
276 fv = g_flags_get_value_by_nick (fclass, flag);
289 *flags_value = value;
296 g_type_class_unref (fclass);
303 parse_knot_from_array (JsonArray *array,
306 if (json_array_get_length (array) != 2)
309 knot->x = json_array_get_int_element (array, 0);
310 knot->y = json_array_get_int_element (array, 1);
316 parse_knot_from_object (JsonObject *object,
319 if (json_object_has_member (object, "x"))
320 knot->x = json_object_get_int_member (object, "x");
324 if (json_object_has_member (object, "y"))
325 knot->y = json_object_get_int_member (object, "y");
333 _clutter_script_parse_knot (ClutterScript *script,
337 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
338 g_return_val_if_fail (node != NULL, FALSE);
339 g_return_val_if_fail (knot != NULL, FALSE);
341 switch (JSON_NODE_TYPE (node))
343 case JSON_NODE_ARRAY:
344 return parse_knot_from_array (json_node_get_array (node), knot);
346 case JSON_NODE_OBJECT:
347 return parse_knot_from_object (json_node_get_object (node), knot);
357 parse_geometry_from_array (JsonArray *array,
358 ClutterGeometry *geometry)
360 if (json_array_get_length (array) != 4)
363 geometry->x = json_array_get_int_element (array, 0);
364 geometry->y = json_array_get_int_element (array, 1);
365 geometry->width = json_array_get_int_element (array, 2);
366 geometry->height = json_array_get_int_element (array, 3);
372 parse_geometry_from_object (JsonObject *object,
373 ClutterGeometry *geometry)
375 if (json_object_has_member (object, "x"))
376 geometry->x = json_object_get_int_member (object, "x");
380 if (json_object_has_member (object, "y"))
381 geometry->y = json_object_get_int_member (object, "y");
385 if (json_object_has_member (object, "width"))
386 geometry->width = json_object_get_int_member (object, "width");
390 if (json_object_has_member (object, "height"))
391 geometry->height = json_object_get_int_member (object, "height");
393 geometry->height = 0;
399 _clutter_script_parse_geometry (ClutterScript *script,
401 ClutterGeometry *geometry)
403 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
404 g_return_val_if_fail (node != NULL, FALSE);
405 g_return_val_if_fail (geometry != NULL, FALSE);
407 switch (JSON_NODE_TYPE (node))
409 case JSON_NODE_ARRAY:
410 return parse_geometry_from_array (json_node_get_array (node), geometry);
412 case JSON_NODE_OBJECT:
413 return parse_geometry_from_object (json_node_get_object (node), geometry);
423 parse_color_from_array (JsonArray *array,
426 if (json_array_get_length (array) != 3 ||
427 json_array_get_length (array) != 4)
430 color->red = CLAMP (json_array_get_int_element (array, 0), 0, 255);
431 color->green = CLAMP (json_array_get_int_element (array, 1), 0, 255);
432 color->blue = CLAMP (json_array_get_int_element (array, 2), 0, 255);
434 if (json_array_get_length (array) == 4)
435 color->alpha = CLAMP (json_array_get_int_element (array, 3), 0, 255);
443 parse_color_from_object (JsonObject *object,
446 if (json_object_has_member (object, "red"))
447 color->red = CLAMP (json_object_get_int_member (object, "red"), 0, 255);
451 if (json_object_has_member (object, "green"))
452 color->green = CLAMP (json_object_get_int_member (object, "green"), 0, 255);
456 if (json_object_has_member (object, "blue"))
457 color->blue = CLAMP (json_object_get_int_member (object, "blue"), 0, 255);
461 if (json_object_has_member (object, "alpha"))
462 color->alpha = CLAMP (json_object_get_int_member (object, "alpha"), 0, 255);
470 _clutter_script_parse_color (ClutterScript *script,
474 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
475 g_return_val_if_fail (node != NULL, FALSE);
476 g_return_val_if_fail (color != NULL, FALSE);
478 switch (JSON_NODE_TYPE (node))
480 case JSON_NODE_ARRAY:
481 return parse_color_from_array (json_node_get_array (node), color);
483 case JSON_NODE_OBJECT:
484 return parse_color_from_object (json_node_get_object (node), color);
486 case JSON_NODE_VALUE:
487 return clutter_color_from_string (color, json_node_get_string (node));
497 parse_point_from_array (JsonArray *array,
500 if (json_array_get_length (array) != 2)
503 point->x = json_array_get_double_element (array, 0);
504 point->y = json_array_get_double_element (array, 1);
510 parse_point_from_object (JsonObject *object,
513 if (json_object_has_member (object, "x"))
514 point->x = json_object_get_double_member (object, "x");
518 if (json_object_has_member (object, "y"))
519 point->y = json_object_get_double_member (object, "y");
527 _clutter_script_parse_point (ClutterScript *script,
531 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
532 g_return_val_if_fail (node != NULL, FALSE);
533 g_return_val_if_fail (point != NULL, FALSE);
535 switch (JSON_NODE_TYPE (node))
537 case JSON_NODE_ARRAY:
538 return parse_point_from_array (json_node_get_array (node), point);
540 case JSON_NODE_OBJECT:
541 return parse_point_from_object (json_node_get_object (node), point);
551 parse_size_from_array (JsonArray *array,
554 if (json_array_get_length (array) != 2)
557 size->width = json_array_get_double_element (array, 0);
558 size->height = json_array_get_double_element (array, 1);
564 parse_size_from_object (JsonObject *object,
567 if (json_object_has_member (object, "width"))
568 size->width = json_object_get_double_member (object, "width");
572 if (json_object_has_member (object, "height"))
573 size->height = json_object_get_double_member (object, "height");
581 _clutter_script_parse_size (ClutterScript *script,
585 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
586 g_return_val_if_fail (node != NULL, FALSE);
587 g_return_val_if_fail (size != NULL, FALSE);
589 switch (JSON_NODE_TYPE (node))
591 case JSON_NODE_ARRAY:
592 return parse_size_from_array (json_node_get_array (node), size);
594 case JSON_NODE_OBJECT:
595 return parse_size_from_object (json_node_get_object (node), size);
605 _clutter_script_get_id_from_node (JsonNode *node)
609 switch (JSON_NODE_TYPE (node))
611 case JSON_NODE_OBJECT:
612 object = json_node_get_object (node);
613 if (json_object_has_member (object, "id"))
614 return json_object_get_string_member (object, "id");
617 case JSON_NODE_VALUE:
618 return json_node_get_string (node);
628 parse_children (ObjectInfo *oinfo,
635 if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
638 retval = oinfo->children;
640 array = json_node_get_array (node);
641 array_len = json_array_get_length (array);
643 for (i = 0; i < array_len; i++)
645 JsonNode *child = json_array_get_element (array, i);
648 id_ = _clutter_script_get_id_from_node (child);
650 retval = g_list_prepend (retval, g_strdup (id_));
653 return g_list_reverse (retval);
657 parse_signals (ClutterScript *script,
665 if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
667 _clutter_script_warn_invalid_value (script, "signals", "Array", node);
671 retval = oinfo->signals;
672 array = json_node_get_array (node);
673 array_len = json_array_get_length (array);
675 for (i = 0; i < array_len; i++)
677 JsonNode *val = json_array_get_element (array, i);
678 SignalInfo *sinfo = NULL;
682 if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT)
684 _clutter_script_warn_invalid_value (script,
685 "signals array", "Object",
690 object = json_node_get_object (val);
692 /* mandatory: "name" */
693 if (!json_object_has_member (object, "name"))
695 _clutter_script_warn_missing_attribute (script, NULL, "name");
700 name = json_object_get_string_member (object, "name");
703 _clutter_script_warn_invalid_value (script,
710 /* mandatory: "target-state" or "handler" */
711 if (json_object_has_member (object, "target-state"))
713 const gchar *state = NULL;
714 const gchar *target = NULL;
715 gboolean warp_to = FALSE;
717 target = json_object_get_string_member (object, "target-state");
720 _clutter_script_warn_invalid_value (script,
721 "target-state", "string",
726 if (json_object_has_member (object, "states"))
727 state = json_object_get_string_member (object, "states");
729 if (json_object_has_member (object, "warp"))
730 warp_to = json_object_get_boolean_member (object, "warp");
732 CLUTTER_NOTE (SCRIPT,
733 "Added signal '%s' (states:%s, target-state:%s, warp:%s)",
735 state != NULL ? state : "<default>", target,
736 warp_to ? "true" : "false");
738 sinfo = g_slice_new0 (SignalInfo);
739 sinfo->is_handler = FALSE;
740 sinfo->name = g_strdup (name);
741 sinfo->state = g_strdup (state);
742 sinfo->target = g_strdup (target);
743 sinfo->warp_to = warp_to;
745 else if (json_object_has_member (object, "handler"))
747 const gchar *handler;
748 const gchar *connect;
749 GConnectFlags flags = 0;
751 handler = json_object_get_string_member (object, "handler");
754 _clutter_script_warn_invalid_value (script,
760 /* optional: "object" */
761 if (json_object_has_member (object, "object"))
762 connect = json_object_get_string_member (object, "object");
766 /* optional: "after" */
767 if (json_object_has_member (object, "after"))
769 if (json_object_get_boolean_member (object, "after"))
770 flags |= G_CONNECT_AFTER;
773 /* optional: "swapped" */
774 if (json_object_has_member (object, "swapped"))
776 if (json_object_get_boolean_member (object, "swapped"))
777 flags |= G_CONNECT_SWAPPED;
780 CLUTTER_NOTE (SCRIPT,
781 "Added signal '%s' (handler:%s, object:%s, flags:%d)",
783 handler, connect, flags);
785 sinfo = g_slice_new0 (SignalInfo);
786 sinfo->is_handler = TRUE;
787 sinfo->name = g_strdup (name);
788 sinfo->handler = g_strdup (handler);
789 sinfo->object = g_strdup (connect);
790 sinfo->flags = flags;
793 _clutter_script_warn_missing_attribute (script,
797 retval = g_list_prepend (retval, sinfo);
803 static ClutterTimeline *
804 construct_timeline (ClutterScript *script,
807 ClutterTimeline *retval = NULL;
811 /* we fake an ObjectInfo so we can reuse clutter_script_construct_object()
812 * here; we do not save it inside the hash table, because if this had
813 * been a named object then we wouldn't have ended up here in the first
816 oinfo = g_slice_new0 (ObjectInfo);
817 oinfo->gtype = CLUTTER_TYPE_TIMELINE;
818 oinfo->id = g_strdup ("dummy");
820 members = json_object_get_members (object);
821 for (l = members; l != NULL; l = l->next)
823 const gchar *name = l->data;
824 JsonNode *node = json_object_get_member (object, name);
825 PropertyInfo *pinfo = g_slice_new0 (PropertyInfo);
827 pinfo->name = g_strdelimit (g_strdup (name), G_STR_DELIMITERS, '-');
828 pinfo->node = json_node_copy (node);
830 oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
833 g_list_free (members);
835 _clutter_script_construct_object (script, oinfo);
836 _clutter_script_apply_properties (script, oinfo);
837 retval = CLUTTER_TIMELINE (oinfo->object);
839 /* we transfer ownership to the alpha function, so we ref before
840 * destroying the ObjectInfo to avoid the timeline going away
842 g_object_ref (retval);
843 object_info_free (oinfo);
848 /* define the names of the animation modes to match the ones
849 * that developers might be more accustomed to
854 ClutterAnimationMode mode;
855 } animation_modes[] = {
856 { "linear", CLUTTER_LINEAR },
857 { "easeInQuad", CLUTTER_EASE_IN_QUAD },
858 { "easeOutQuad", CLUTTER_EASE_OUT_QUAD },
859 { "easeInOutQuad", CLUTTER_EASE_IN_OUT_QUAD },
860 { "easeInCubic", CLUTTER_EASE_IN_CUBIC },
861 { "easeOutCubic", CLUTTER_EASE_OUT_CUBIC },
862 { "easeInOutCubic", CLUTTER_EASE_IN_OUT_CUBIC },
863 { "easeInQuart", CLUTTER_EASE_IN_QUART },
864 { "easeOutQuart", CLUTTER_EASE_OUT_QUART },
865 { "easeInOutQuart", CLUTTER_EASE_IN_OUT_QUART },
866 { "easeInQuint", CLUTTER_EASE_IN_QUINT },
867 { "easeOutQuint", CLUTTER_EASE_OUT_QUINT },
868 { "easeInOutQuint", CLUTTER_EASE_IN_OUT_QUINT },
869 { "easeInSine", CLUTTER_EASE_IN_SINE },
870 { "easeOutSine", CLUTTER_EASE_OUT_SINE },
871 { "easeInOutSine", CLUTTER_EASE_IN_OUT_SINE },
872 { "easeInExpo", CLUTTER_EASE_IN_EXPO },
873 { "easeOutExpo", CLUTTER_EASE_OUT_EXPO },
874 { "easeInOutExpo", CLUTTER_EASE_IN_OUT_EXPO },
875 { "easeInCirc", CLUTTER_EASE_IN_CIRC },
876 { "easeOutCirc", CLUTTER_EASE_OUT_CIRC },
877 { "easeInOutCirc", CLUTTER_EASE_IN_OUT_CIRC },
878 { "easeInElastic", CLUTTER_EASE_IN_ELASTIC },
879 { "easeOutElastic", CLUTTER_EASE_OUT_ELASTIC },
880 { "easeInOutElastic", CLUTTER_EASE_IN_OUT_ELASTIC },
881 { "easeInBack", CLUTTER_EASE_IN_BACK },
882 { "easeOutBack", CLUTTER_EASE_OUT_BACK },
883 { "easeInOutBack", CLUTTER_EASE_IN_OUT_BACK },
884 { "easeInBounce", CLUTTER_EASE_IN_BOUNCE },
885 { "easeOutBounce", CLUTTER_EASE_OUT_BOUNCE },
886 { "easeInOutBounce", CLUTTER_EASE_IN_OUT_BOUNCE },
889 static const gint n_animation_modes = G_N_ELEMENTS (animation_modes);
892 _clutter_script_resolve_animation_mode (JsonNode *node)
894 gint i, res = CLUTTER_CUSTOM_MODE;
896 if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
897 return CLUTTER_CUSTOM_MODE;
899 if (json_node_get_value_type (node) == G_TYPE_INT64)
900 return json_node_get_int (node);
902 if (json_node_get_value_type (node) == G_TYPE_STRING)
904 const gchar *name = json_node_get_string (node);
906 /* XXX - we might be able to optimize by changing the ordering
907 * of the animation_modes array, e.g.
908 * - special casing linear
909 * - tokenizing ('ease', 'In', 'Sine') and matching on token
910 * - binary searching?
912 for (i = 0; i < n_animation_modes; i++)
914 if (strcmp (animation_modes[i].name, name) == 0)
915 return animation_modes[i].mode;
918 if (_clutter_script_enum_from_string (CLUTTER_TYPE_ANIMATION_MODE,
923 g_warning ("Unable to find the animation mode '%s'", name);
926 return CLUTTER_CUSTOM_MODE;
929 static ClutterAlphaFunc
930 resolve_alpha_func (const gchar *name)
932 static GModule *module = NULL;
933 ClutterAlphaFunc func;
935 CLUTTER_NOTE (SCRIPT, "Looking up '%s' alpha function", name);
937 if (G_UNLIKELY (!module))
938 module = g_module_open (NULL, 0);
940 if (g_module_symbol (module, name, (gpointer) &func))
942 CLUTTER_NOTE (SCRIPT, "Found '%s' alpha function in the symbols table",
951 _clutter_script_parse_alpha (ClutterScript *script,
954 GObject *retval = NULL;
956 ClutterTimeline *timeline = NULL;
957 ClutterAlphaFunc alpha_func = NULL;
958 ClutterAnimationMode mode = CLUTTER_CUSTOM_MODE;
960 gboolean unref_timeline = FALSE;
962 if (JSON_NODE_TYPE (node) != JSON_NODE_OBJECT)
965 object = json_node_get_object (node);
967 val = json_object_get_member (object, "timeline");
970 if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE &&
971 json_node_get_string (val) != NULL)
973 const gchar *id_ = json_node_get_string (val);
976 CLUTTER_TIMELINE (clutter_script_get_object (script, id_));
978 else if (JSON_NODE_TYPE (val) == JSON_NODE_OBJECT)
980 timeline = construct_timeline (script, json_node_get_object (val));
981 unref_timeline = TRUE;
985 val = json_object_get_member (object, "mode");
987 mode = _clutter_script_resolve_animation_mode (val);
989 if (mode == CLUTTER_CUSTOM_MODE)
991 val = json_object_get_member (object, "function");
992 if (val && json_node_get_string (val) != NULL)
994 alpha_func = resolve_alpha_func (json_node_get_string (val));
997 g_warning ("Unable to find the function '%s' in the "
998 "Clutter alpha functions or the symbols table",
999 json_node_get_string (val));
1004 CLUTTER_NOTE (SCRIPT, "Parsed alpha: %s timeline (%p) (mode:%d, func:%p)",
1005 unref_timeline ? "implicit" : "explicit",
1006 timeline ? timeline : 0x0,
1007 mode != CLUTTER_CUSTOM_MODE ? mode : 0,
1008 alpha_func ? alpha_func : 0x0);
1010 retval = g_object_new (CLUTTER_TYPE_ALPHA, NULL);
1012 if (mode != CLUTTER_CUSTOM_MODE)
1013 clutter_alpha_set_mode (CLUTTER_ALPHA (retval), mode);
1015 if (alpha_func != NULL)
1016 clutter_alpha_set_func (CLUTTER_ALPHA (retval), alpha_func, NULL, NULL);
1018 clutter_alpha_set_timeline (CLUTTER_ALPHA (retval), timeline);
1020 /* if we created an implicit timeline, the Alpha has full ownership
1021 * of it now, since it won't be accessible from ClutterScript
1024 g_object_unref (timeline);
1030 clutter_script_parser_object_end (JsonParser *json_parser,
1033 ClutterScriptParser *parser = CLUTTER_SCRIPT_PARSER (json_parser);
1034 ClutterScript *script = parser->script;
1040 /* if the object definition does not have an 'id' field we'll
1041 * fake one for it...
1043 if (!json_object_has_member (object, "id"))
1047 /* ... unless it doesn't even have a type - in which case
1048 * it is an internal object definition and we're not
1049 * supposed to touch it
1051 if (!json_object_has_member (object, "type"))
1054 fake = _clutter_script_generate_fake_id (script);
1055 json_object_set_string_member (object, "id", fake);
1057 CLUTTER_NOTE (SCRIPT,
1058 "Adding fake id '%s' to object of type '%s'",
1059 json_object_get_string_member (object, "id"),
1060 json_object_get_string_member (object, "type"));
1065 if (!json_object_has_member (object, "type"))
1067 val = json_object_get_member (object, "id");
1069 _clutter_script_warn_missing_attribute (script,
1070 json_node_get_string (val),
1075 id_ = json_object_get_string_member (object, "id");
1076 CLUTTER_NOTE (SCRIPT, "Getting object info for object '%s'", id_);
1078 oinfo = _clutter_script_get_object_info (script, id_);
1081 const gchar *class_name;
1083 oinfo = g_slice_new0 (ObjectInfo);
1084 oinfo->merge_id = _clutter_script_get_last_merge_id (script);
1085 oinfo->id = g_strdup (id_);
1087 class_name = json_object_get_string_member (object, "type");
1088 oinfo->class_name = g_strdup (class_name);
1090 if (json_object_has_member (object, "type_func"))
1092 const gchar *type_func;
1094 type_func = json_object_get_string_member (object, "type_func");
1095 oinfo->type_func = g_strdup (type_func);
1097 /* remove the type_func member; we don't want it to
1098 * pollute the object members
1100 json_object_remove_member (object, "type_func");
1104 if (json_object_has_member (object, "children"))
1106 val = json_object_get_member (object, "children");
1107 oinfo->children = parse_children (oinfo, val);
1109 json_object_remove_member (object, "children");
1112 if (json_object_has_member (object, "signals"))
1114 val = json_object_get_member (object, "signals");
1115 oinfo->signals = parse_signals (script, oinfo, val);
1117 json_object_remove_member (object, "signals");
1120 oinfo->is_actor = FALSE;
1122 if (strcmp (oinfo->class_name, "ClutterStage") == 0 &&
1123 json_object_has_member (object, "is-default"))
1125 oinfo->is_actor = TRUE;
1126 oinfo->is_stage = TRUE;
1127 oinfo->is_stage_default =
1128 json_object_get_boolean_member (object, "is-default");
1130 json_object_remove_member (object, "is-default");
1133 oinfo->is_stage_default = FALSE;
1135 oinfo->is_unmerged = FALSE;
1136 oinfo->has_unresolved = TRUE;
1138 members = json_object_get_members (object);
1139 for (l = members; l; l = l->next)
1141 const gchar *name = l->data;
1142 PropertyInfo *pinfo;
1145 CLUTTER_NOTE (SCRIPT, "Object '%s' member '%s'",
1149 /* we have already parsed these */
1150 if (strcmp (name, "id") == 0 || strcmp (name, "type") == 0)
1153 node = json_object_get_member (object, name);
1155 /* this should not really happen; we're getting a list of
1156 * member names, and if one does not map a real member
1157 * value then it's likely that something has gone wrong
1159 if (G_UNLIKELY (node == NULL))
1161 CLUTTER_NOTE (SCRIPT,
1162 "Empty node for member '%s' of object '%s' (type: %s)",
1169 pinfo = g_slice_new (PropertyInfo);
1171 pinfo->name = g_strdup (name);
1172 pinfo->node = json_node_copy (node);
1173 pinfo->pspec = NULL;
1174 pinfo->is_child = g_str_has_prefix (name, "child::") ? TRUE : FALSE;
1175 pinfo->is_layout = g_str_has_prefix (name, "layout::") ? TRUE : FALSE;
1177 oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
1180 g_list_free (members);
1182 CLUTTER_NOTE (SCRIPT,
1183 "Added object '%s' (type:%s, id:%d, props:%d, signals:%d)",
1187 g_list_length (oinfo->properties),
1188 g_list_length (oinfo->signals));
1190 _clutter_script_add_object_info (script, oinfo);
1191 _clutter_script_construct_object (script, oinfo);
1195 clutter_script_parser_parse_end (JsonParser *parser)
1197 clutter_script_ensure_objects (CLUTTER_SCRIPT_PARSER (parser)->script);
1201 _clutter_script_parse_translatable_string (ClutterScript *script,
1206 const char *string, *domain, *context;
1208 gboolean translatable;
1210 if (!JSON_NODE_HOLDS_OBJECT (node))
1213 obj = json_node_get_object (node);
1214 if (!(json_object_has_member (obj, "translatable") &&
1215 json_object_has_member (obj, "string")))
1218 translatable = json_object_get_boolean_member (obj, "translatable");
1220 string = json_object_get_string_member (obj, "string");
1221 if (string == NULL || *string == '\0')
1224 if (json_object_has_member (obj, "context"))
1225 context = json_object_get_string_member (obj, "context");
1229 if (json_object_has_member (obj, "domain"))
1230 domain = json_object_get_string_member (obj, "domain");
1234 if (domain == NULL || *domain == '\0')
1235 domain = clutter_script_get_translation_domain (script);
1239 if (context != NULL && *context != '\0')
1240 res = g_dpgettext2 (domain, context, string);
1242 res = g_dgettext (domain, string);
1248 *str = g_strdup (res);
1254 _clutter_script_parse_node (ClutterScript *script,
1260 GValue node_value = G_VALUE_INIT;
1261 gboolean retval = FALSE;
1263 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
1264 g_return_val_if_fail (name != NULL, FALSE);
1265 g_return_val_if_fail (node != NULL, FALSE);
1267 switch (JSON_NODE_TYPE (node))
1269 case JSON_NODE_OBJECT:
1270 /* if we don't have a GParamSpec we can't infer the type
1271 * of the property; this usually means that this property
1272 * is a custom member that will be parsed by the Scriptable
1273 * interface implementantion
1275 if (pspec == NULL && !G_IS_VALUE (value))
1283 if (G_IS_VALUE (value))
1284 p_type = G_VALUE_TYPE (value);
1287 p_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
1288 g_value_init (value, p_type);
1291 if (g_type_is_a (p_type, G_TYPE_OBJECT))
1293 /* default GObject handling: we get the id and
1294 * retrieve the ObjectInfo for it; since the object
1295 * definitions are parsed leaf-first we are guaranteed
1296 * to have a defined object at this point
1298 id_ = _clutter_script_get_id_from_node (node);
1299 if (id_ == NULL || *id_ == '\0')
1302 oinfo = _clutter_script_get_object_info (script, id_);
1303 if (oinfo == NULL || oinfo->gtype == G_TYPE_INVALID )
1306 if (g_type_is_a (oinfo->gtype, p_type))
1308 /* force construction, even though it should
1309 * not be necessary; we don't need the properties
1310 * to be applied as well: they will when the
1311 * ScriptParser finishes
1313 _clutter_script_construct_object (script, oinfo);
1315 g_value_set_object (value, oinfo->object);
1320 else if (p_type == CLUTTER_TYPE_KNOT)
1322 ClutterKnot knot = { 0, };
1324 /* knot := { "x" : (int), "y" : (int) } */
1326 if (_clutter_script_parse_knot (script, node, &knot))
1328 g_value_set_boxed (value, &knot);
1332 else if (p_type == CLUTTER_TYPE_GEOMETRY)
1334 ClutterGeometry geom = { 0, };
1344 if (_clutter_script_parse_geometry (script, node, &geom))
1346 g_value_set_boxed (value, &geom);
1350 else if (p_type == CLUTTER_TYPE_COLOR)
1352 ClutterColor color = { 0, };
1362 if (_clutter_script_parse_color (script, node, &color))
1364 g_value_set_boxed (value, &color);
1368 else if (p_type == CLUTTER_TYPE_POINT)
1370 ClutterPoint point = CLUTTER_POINT_INIT_ZERO;
1372 if (_clutter_script_parse_point (script, node, &point))
1374 g_value_set_boxed (value, &point);
1378 else if (p_type == CLUTTER_TYPE_SIZE)
1380 ClutterSize size = CLUTTER_SIZE_INIT_ZERO;
1382 if (_clutter_script_parse_size (script, node, &size))
1384 g_value_set_boxed (value, &size);
1388 else if (p_type == G_TYPE_STRING)
1392 if (_clutter_script_parse_translatable_string (script, node, &str))
1394 g_value_take_string (value, str);
1401 case JSON_NODE_ARRAY:
1402 if (!pspec && !G_IS_VALUE (value))
1406 if (!G_IS_VALUE (value))
1407 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1409 if (G_VALUE_HOLDS (value, CLUTTER_TYPE_KNOT))
1411 ClutterKnot knot = { 0, };
1413 /* knot := [ (int), (int) ] */
1415 if (_clutter_script_parse_knot (script, node, &knot))
1417 g_value_set_boxed (value, &knot);
1421 else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_GEOMETRY))
1423 ClutterGeometry geom = { 0, };
1425 /* geometry := [ (int), (int), (int), (int) ] */
1427 if (_clutter_script_parse_geometry (script, node, &geom))
1429 g_value_set_boxed (value, &geom);
1433 else if (CLUTTER_VALUE_HOLDS_COLOR (value))
1435 ClutterColor color = { 0, };
1437 /* color := [ (int), (int), (int), (int) ] */
1439 if (_clutter_script_parse_color (script, node, &color))
1441 g_value_set_boxed (value, &color);
1445 else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_POINT))
1447 ClutterPoint point = CLUTTER_POINT_INIT_ZERO;
1449 if (_clutter_script_parse_point (script, node, &point))
1451 g_value_set_boxed (value, &point);
1455 else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_SIZE))
1457 ClutterSize size = CLUTTER_SIZE_INIT_ZERO;
1459 if (_clutter_script_parse_size (script, node, &size))
1461 g_value_set_boxed (value, &size);
1465 else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
1467 JsonArray *array = json_node_get_array (node);
1468 guint i, array_len = json_array_get_length (array);
1469 GPtrArray *str_array = g_ptr_array_sized_new (array_len);
1471 /* strv := [ (str), (str), ... ] */
1473 for (i = 0; i < array_len; i++)
1475 JsonNode *val = json_array_get_element (array, i);
1477 if (JSON_NODE_TYPE (val) != JSON_NODE_VALUE &&
1478 json_node_get_string (val) == NULL)
1481 g_ptr_array_add (str_array,
1482 (gpointer) json_node_get_string (val));
1485 g_value_set_boxed (value, str_array->pdata);
1486 g_ptr_array_free (str_array, TRUE);
1493 case JSON_NODE_NULL:
1496 case JSON_NODE_VALUE:
1497 json_node_get_value (node, &node_value);
1499 if (!pspec && !G_IS_VALUE (value))
1500 g_value_init (value, G_VALUE_TYPE (&node_value));
1502 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1504 switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)))
1506 /* fundamental JSON types */
1510 case G_TYPE_BOOLEAN:
1511 g_value_copy (&node_value, value);
1516 g_value_set_int (value, g_value_get_int64 (&node_value));
1521 g_value_set_uint (value, (guint) g_value_get_int64 (&node_value));
1526 g_value_set_ulong (value, (gulong) g_value_get_int64 (&node_value));
1531 g_value_set_uchar (value, (guchar) g_value_get_int64 (&node_value));
1536 if (G_VALUE_HOLDS (&node_value, G_TYPE_DOUBLE))
1538 g_value_set_float (value, g_value_get_double (&node_value));
1541 else if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
1543 g_value_set_float (value, g_value_get_int64 (&node_value));
1549 if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
1551 g_value_set_enum (value, g_value_get_int64 (&node_value));
1554 else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1558 retval = _clutter_script_enum_from_string (G_VALUE_TYPE (value),
1559 g_value_get_string (&node_value),
1562 g_value_set_enum (value, enum_value);
1567 if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
1569 g_value_set_flags (value, g_value_get_int64 (&node_value));
1572 else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1576 retval = _clutter_script_flags_from_string (G_VALUE_TYPE (value),
1577 g_value_get_string (&node_value),
1580 g_value_set_flags (value, flags_value);
1585 if (G_VALUE_HOLDS (value, CLUTTER_TYPE_COLOR))
1587 ClutterColor color = { 0, };
1589 retval = _clutter_script_parse_color (script, node, &color);
1591 clutter_value_set_color (value, &color);
1596 if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1598 const gchar *str = g_value_get_string (&node_value);
1599 GObject *object = clutter_script_get_object (script, str);
1602 CLUTTER_NOTE (SCRIPT,
1603 "Assigning '%s' (%s) to property '%s'",
1605 G_OBJECT_TYPE_NAME (object),
1608 g_value_set_object (value, object);
1619 if (G_VALUE_TYPE (value) == G_TYPE_GTYPE &&
1620 G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1622 const gchar *str = g_value_get_string (&node_value);
1623 GType type = clutter_script_get_type_from_name (script, str);
1624 g_value_set_gtype (value, type);
1628 g_value_unset (&node_value);
1636 clutter_script_translate_parameters (ClutterScript *script,
1642 ClutterScriptable *scriptable = NULL;
1643 ClutterScriptableIface *iface = NULL;
1644 GList *l, *unparsed;
1645 gboolean parse_custom = FALSE;
1647 *params = g_array_new (FALSE, FALSE, sizeof (GParameter));
1649 if (CLUTTER_IS_SCRIPTABLE (object))
1651 scriptable = CLUTTER_SCRIPTABLE (object);
1652 iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
1654 if (iface->parse_custom_node)
1655 parse_custom = TRUE;
1660 for (l = properties; l != NULL; l = l->next)
1662 PropertyInfo *pinfo = l->data;
1663 GParameter param = { NULL };
1664 gboolean res = FALSE;
1666 if (pinfo->is_child || pinfo->is_layout)
1668 CLUTTER_NOTE (SCRIPT, "Skipping %s property '%s'",
1669 pinfo->is_child ? "child" : "layout",
1671 unparsed = g_list_prepend (unparsed, pinfo);
1675 CLUTTER_NOTE (SCRIPT, "Parsing %s property (id:%s)",
1676 pinfo->pspec ? "regular" : "custom",
1680 res = iface->parse_custom_node (scriptable, script, ¶m.value,
1685 res = _clutter_script_parse_node (script, ¶m.value,
1692 CLUTTER_NOTE (SCRIPT, "Property '%s' ignored", pinfo->name);
1693 unparsed = g_list_prepend (unparsed, pinfo);
1697 param.name = g_strdup (pinfo->name);
1699 g_array_append_val (*params, param);
1701 property_info_free (pinfo);
1704 g_list_free (properties);
1710 clutter_script_construct_parameters (ClutterScript *script,
1714 GArray **construct_params)
1716 GObjectClass *klass;
1717 GList *l, *unparsed;
1719 klass = g_type_class_ref (gtype);
1720 g_assert (klass != NULL);
1722 *construct_params = g_array_new (FALSE, FALSE, sizeof (GParameter));
1726 for (l = properties; l != NULL; l = l->next)
1728 PropertyInfo *pinfo = l->data;
1729 GParameter param = { NULL };
1730 GParamSpec *pspec = NULL;
1732 /* we allow custom property names for classes, so if we
1733 * don't find a corresponding GObject property for this
1734 * class we just skip it and let the class itself deal
1737 pspec = g_object_class_find_property (klass, pinfo->name);
1739 pinfo->pspec = g_param_spec_ref (pspec);
1742 pinfo->pspec = NULL;
1743 unparsed = g_list_prepend (unparsed, pinfo);
1747 if (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY))
1749 unparsed = g_list_prepend (unparsed, pinfo);
1753 param.name = g_strdup (pinfo->name);
1755 if (!_clutter_script_parse_node (script, ¶m.value,
1760 unparsed = g_list_prepend (unparsed, pinfo);
1764 g_array_append_val (*construct_params, param);
1766 property_info_free (pinfo);
1769 g_list_free (properties);
1771 g_type_class_unref (klass);
1777 apply_layout_properties (ClutterScript *script,
1778 ClutterContainer *container,
1779 ClutterActor *actor,
1782 ClutterScriptable *scriptable = NULL;
1783 ClutterScriptableIface *iface = NULL;
1784 gboolean set_custom_property = FALSE;
1785 gboolean parse_custom_node = FALSE;
1786 GList *l, *unresolved, *properties;
1787 ClutterLayoutManager *manager;
1790 manager = g_object_get_data (G_OBJECT (container), "clutter-layout-manager");
1791 if (manager == NULL)
1794 meta_type = _clutter_layout_manager_get_child_meta_type (manager);
1795 if (meta_type == G_TYPE_INVALID)
1798 CLUTTER_NOTE (SCRIPT, "Layout manager of type '%s' with meta type '%s'",
1799 G_OBJECT_TYPE_NAME (manager),
1800 g_type_name (meta_type));
1802 /* shortcut, to avoid typechecking every time */
1803 if (CLUTTER_IS_SCRIPTABLE (manager))
1805 scriptable = CLUTTER_SCRIPTABLE (manager);
1806 iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
1808 parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE;
1809 set_custom_property = iface->set_custom_property != NULL ? TRUE : FALSE;
1812 properties = oinfo->properties;
1813 oinfo->properties = NULL;
1816 for (l = properties; l != NULL; l = l->next)
1818 PropertyInfo *pinfo = l->data;
1819 GValue value = G_VALUE_INIT;
1820 gboolean res = FALSE;
1823 if (!pinfo->is_layout)
1825 unresolved = g_list_prepend (unresolved, pinfo);
1829 name = pinfo->name + strlen ("layout::");
1832 clutter_layout_manager_find_child_property (manager, name);
1834 if (pinfo->pspec != NULL)
1835 g_param_spec_ref (pinfo->pspec);
1837 CLUTTER_NOTE (SCRIPT, "Parsing %s layout property (id:%s)",
1838 pinfo->pspec != NULL ? "regular" : "custom",
1841 if (parse_custom_node)
1842 res = iface->parse_custom_node (scriptable, script, &value,
1847 res = _clutter_script_parse_node (script, &value,
1854 CLUTTER_NOTE (SCRIPT, "Layout property '%s' ignored", name);
1855 unresolved = g_list_prepend (unresolved, pinfo);
1859 CLUTTER_NOTE (SCRIPT,
1860 "Setting %s layout property '%s' (type:%s) to "
1861 "object '%s' (id:%s)",
1862 set_custom_property ? "custom" : "regular",
1864 g_type_name (G_VALUE_TYPE (&value)),
1865 g_type_name (oinfo->gtype),
1868 clutter_layout_manager_child_set_property (manager, container, actor,
1872 g_value_unset (&value);
1874 property_info_free (pinfo);
1877 g_list_free (properties);
1879 oinfo->properties = unresolved;
1883 apply_child_properties (ClutterScript *script,
1884 ClutterContainer *container,
1885 ClutterActor *actor,
1888 ClutterScriptable *scriptable = NULL;
1889 ClutterScriptableIface *iface = NULL;
1890 gboolean set_custom_property = FALSE;
1891 gboolean parse_custom_node = FALSE;
1892 GList *l, *unresolved, *properties;
1893 GObjectClass *klass;
1896 meta_type = CLUTTER_CONTAINER_GET_IFACE (container)->child_meta_type;
1897 if (meta_type == G_TYPE_INVALID)
1900 klass = G_OBJECT_GET_CLASS (container);
1902 /* shortcut, to avoid typechecking every time */
1903 if (CLUTTER_IS_SCRIPTABLE (container))
1905 scriptable = CLUTTER_SCRIPTABLE (container);
1906 iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
1908 parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE;
1909 set_custom_property = iface->set_custom_property != NULL ? TRUE : FALSE;
1912 properties = oinfo->properties;
1913 oinfo->properties = NULL;
1916 for (l = properties; l != NULL; l = l->next)
1918 PropertyInfo *pinfo = l->data;
1919 GValue value = G_VALUE_INIT;
1920 gboolean res = FALSE;
1923 if (!pinfo->is_child)
1925 unresolved = g_list_prepend (unresolved, pinfo);
1929 name = pinfo->name + strlen ("child::");
1932 clutter_container_class_find_child_property (klass, name);
1934 if (pinfo->pspec != NULL)
1935 g_param_spec_ref (pinfo->pspec);
1937 CLUTTER_NOTE (SCRIPT, "Parsing %s child property (id:%s)",
1938 pinfo->pspec != NULL ? "regular" : "custom",
1941 if (parse_custom_node)
1942 res = iface->parse_custom_node (scriptable, script, &value,
1947 res = _clutter_script_parse_node (script, &value,
1954 CLUTTER_NOTE (SCRIPT, "Child property '%s' ignored", name);
1955 unresolved = g_list_prepend (unresolved, pinfo);
1960 CLUTTER_NOTE (SCRIPT,
1961 "Setting %s child property '%s' (type:%s) to "
1962 "object '%s' (id:%s)",
1963 set_custom_property ? "custom" : "regular",
1965 g_type_name (G_VALUE_TYPE (&value)),
1966 g_type_name (oinfo->gtype),
1969 clutter_container_child_set_property (container, actor,
1973 g_value_unset (&value);
1975 property_info_free (pinfo);
1978 g_list_free (properties);
1980 oinfo->properties = unresolved;
1984 add_children (ClutterScript *script,
1987 ClutterContainer *container = CLUTTER_CONTAINER (oinfo->object);
1988 GList *l, *unresolved;
1991 for (l = oinfo->children; l != NULL; l = l->next)
1993 const gchar *name = l->data;
1994 GObject *object = NULL;
1995 ObjectInfo *child_info;
1997 child_info = _clutter_script_get_object_info (script, name);
1998 if (child_info != NULL)
2000 _clutter_script_construct_object (script, child_info);
2001 object = child_info->object;
2006 unresolved = g_list_prepend (unresolved, g_strdup (name));
2010 if (!CLUTTER_IS_ACTOR (object))
2012 g_warning ("The object definition '%s' (type: %s) is not "
2013 "an actor, but it is referenced in the 'children' "
2014 "member of the container '%s' (type: %s); skipping.",
2016 g_type_name (child_info->gtype),
2018 g_type_name (oinfo->gtype));
2022 CLUTTER_NOTE (SCRIPT, "Adding children '%s' to actor of type '%s'",
2024 g_type_name (G_OBJECT_TYPE (container)));
2026 clutter_container_add_actor (container, CLUTTER_ACTOR (object));
2029 g_list_foreach (oinfo->children, (GFunc) g_free, NULL);
2030 g_list_free (oinfo->children);
2032 oinfo->children = unresolved;
2036 _clutter_script_check_unresolved (ClutterScript *script,
2039 if (oinfo->children != NULL && CLUTTER_IS_CONTAINER (oinfo->object))
2040 add_children (script, oinfo);
2042 /* this is a bit *eugh*, but it allows us to effectively make sure
2043 * that child and layout properties are parsed and applied to the
2046 if (oinfo->properties != NULL && CLUTTER_IS_ACTOR (oinfo->object))
2048 ClutterActor *parent;
2050 parent = clutter_actor_get_parent (CLUTTER_ACTOR (oinfo->object));
2053 ClutterContainer *container = CLUTTER_CONTAINER (parent);
2054 ClutterActor *child;
2056 for (child = clutter_actor_get_first_child (parent);
2058 child = clutter_actor_get_next_sibling (child))
2060 ObjectInfo *child_info;
2063 id_ = clutter_get_script_id (G_OBJECT (child));
2064 if (id_ == NULL || *id_ == '\0')
2067 child_info = _clutter_script_get_object_info (script, id_);
2068 if (child_info == NULL)
2071 apply_child_properties (script, container,
2074 apply_layout_properties (script, container,
2081 if (oinfo->properties || oinfo->children)
2082 oinfo->has_unresolved = TRUE;
2084 oinfo->has_unresolved = FALSE;
2088 _clutter_script_apply_properties (ClutterScript *script,
2091 ClutterScriptable *scriptable = NULL;
2092 ClutterScriptableIface *iface = NULL;
2093 gboolean set_custom_property = FALSE;
2094 GObject *object = oinfo->object;
2099 if (!oinfo->has_unresolved)
2102 /* shortcut, to avoid typechecking every time */
2103 if (CLUTTER_IS_SCRIPTABLE (object))
2105 scriptable = CLUTTER_SCRIPTABLE (object);
2106 iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
2108 if (iface->set_custom_property)
2109 set_custom_property = TRUE;
2112 /* then we get the rest of the parameters, asking the object itself
2113 * to translate them for us, if we cannot do that
2115 properties = oinfo->properties;
2116 oinfo->properties = clutter_script_translate_parameters (script,
2122 /* consume all the properties we could translate in this pass */
2123 for (i = 0; i < params->len; i++)
2125 GParameter *param = &g_array_index (params, GParameter, i);
2127 CLUTTER_NOTE (SCRIPT,
2128 "Setting %s property '%s' (type:%s) to object '%s' (id:%s)",
2129 set_custom_property ? "custom" : "regular",
2131 g_type_name (G_VALUE_TYPE (¶m->value)),
2132 g_type_name (oinfo->gtype),
2135 if (set_custom_property)
2136 iface->set_custom_property (scriptable, script,
2140 g_object_set_property (object, param->name, ¶m->value);
2142 g_free ((gchar *) param->name);
2143 g_value_unset (¶m->value);
2146 g_array_free (params, TRUE);
2148 _clutter_script_check_unresolved (script, oinfo);
2152 _clutter_script_construct_object (ClutterScript *script,
2155 GArray *params = NULL;
2158 /* we have completely updated the object */
2159 if (oinfo->object != NULL)
2161 if (oinfo->has_unresolved)
2162 _clutter_script_check_unresolved (script, oinfo);
2167 if (oinfo->gtype == G_TYPE_INVALID)
2169 if (G_UNLIKELY (oinfo->type_func))
2170 oinfo->gtype = _clutter_script_get_type_from_symbol (oinfo->type_func);
2172 oinfo->gtype = clutter_script_get_type_from_name (script, oinfo->class_name);
2174 if (G_UNLIKELY (oinfo->gtype == G_TYPE_INVALID))
2177 oinfo->is_actor = g_type_is_a (oinfo->gtype, CLUTTER_TYPE_ACTOR);
2178 if (oinfo->is_actor)
2179 oinfo->is_stage = g_type_is_a (oinfo->gtype, CLUTTER_TYPE_STAGE);
2182 if (oinfo->is_stage && oinfo->is_stage_default)
2184 ClutterStageManager *manager = clutter_stage_manager_get_default ();
2185 GList *properties = oinfo->properties;
2186 ClutterStage *default_stage;
2188 /* the default stage is a complex beast: we cannot create it using
2189 * g_object_newv() but we need clutter_script_construct_parameters()
2190 * to add the GParamSpec to the PropertyInfo pspec member, so
2191 * that we don't have to implement every complex property (like
2192 * the "color" one) directly inside the ClutterStage class.
2195 clutter_script_construct_parameters (script,
2201 default_stage = clutter_stage_manager_get_default_stage (manager);
2202 oinfo->object = G_OBJECT (default_stage);
2204 for (i = 0; i < params->len; i++)
2206 GParameter *param = &g_array_index (params, GParameter, i);
2208 g_free ((gchar *) param->name);
2209 g_value_unset (¶m->value);
2212 g_array_free (params, TRUE);
2216 GList *properties = oinfo->properties;
2217 GParameter *parameters;
2219 /* every other object: first, we get the construction parameters */
2221 clutter_script_construct_parameters (script,
2227 parameters = (GParameter *) (void *) params->data;
2228 oinfo->object = g_object_newv (oinfo->gtype,
2232 /* by sinking the floating reference, we make sure that the reference
2233 * count is correct whether the object is referenced from somewhere
2234 * else too or only by this ClutterScript object.
2236 g_object_ref_sink (oinfo->object);
2238 for (i = 0; i < params->len; i++)
2240 GParameter *param = &g_array_index (params, GParameter, i);
2242 g_free ((gchar *) param->name);
2243 g_value_unset (¶m->value);
2246 g_array_free (params, TRUE);
2249 g_assert (oinfo->object != NULL);
2251 if (CLUTTER_IS_SCRIPTABLE (oinfo->object))
2252 clutter_scriptable_set_id (CLUTTER_SCRIPTABLE (oinfo->object), oinfo->id);
2254 g_object_set_data_full (oinfo->object, "clutter-script-id",
2255 g_strdup (oinfo->id),
2258 _clutter_script_check_unresolved (script, oinfo);