4 * An OpenGL based 'interactive canvas' library.
6 * Authored By Matthew Allum <mallum@openedhand.com>
8 * Copyright (C) 2006 OpenedHand
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
27 * SECTION:clutter-script
28 * @short_description: Loads a scene from UI definition data
30 * #ClutterScript is an object used for loading and building parts or a
31 * complete scenegraph from external definition data in forms of string
34 * The UI definition format is JSON, the JavaScript Object Notation as
35 * described by RFC 4627. #ClutterScript can load a JSON data stream,
36 * parse it and build all the objects defined into it. Each object must
37 * have an "id" and a "type" properties defining the name to be used
38 * to retrieve it from #ClutterScript with clutter_script_get_object(),
39 * and the class type to be instanciated. Every other attribute will
40 * be mapped to the class properties.
42 * A #ClutterScript holds a reference on every object it creates from
43 * the definition data, except for the stage. Every non-actor object
44 * will be finalized when the #ClutterScript instance holding it will
45 * be finalized, so they need to be referenced using g_object_ref() in
46 * order for them to survive.
48 * A simple object might be defined as:
50 * <informalexample><programlisting><![CDATA[
52 * "id" : "red-button",
53 * "type" : "ClutterRectangle",
56 * "color" : "#ff0000ff"
58 * ]]></programlisting></informalexample>
60 * This will produce a red #ClutterRectangle, 100x100 pixels wide, and
61 * with a ClutterScript id of "red-button"; it can be retrieved by calling:
64 * ClutterActor *red_button;
66 * red_button = CLUTTER_ACTOR (clutter_script_get_object (script, "red-button"));
69 * and then manipulated with the Clutter API. For every object created
70 * using ClutterScript it is possible to check the id by calling
71 * clutter_get_script_id().
73 * Packing can be represented using the "children" member, and passing an
74 * array of objects or ids of objects already defined (but not packed: the
75 * packing rules of Clutter still apply, and an actor cannot be packed
76 * in multiple containers without unparenting it in between).
78 * Behaviours and timelines can also be defined inside a UI definition
81 * <informalexample><programlisting><![CDATA[
83 * "id" : "rotate-behaviour",
84 * "type" : "ClutterBehaviourRotate",
85 * "angle-start" : 0.0,
86 * "angle-end" : 360.0,
89 * "timeline" : { "duration" : 4000, "loop" : true },
90 * "mode" : "easeInSine"
93 * ]]></programlisting></informalexample>
95 * And then to apply a defined behaviour to an actor defined inside the
96 * definition of an actor, the "behaviour" member can be used:
98 * <informalexample><programlisting><![CDATA[
100 * "id" : "my-rotating-actor",
101 * "type" : "ClutterTexture",
103 * "behaviours" : [ "rotate-behaviour" ]
105 * ]]></programlisting></informalexample>
107 * A #ClutterAlpha belonging to a #ClutterBehaviour can only be defined
108 * implicitly like in the example above, or explicitly by setting the
109 * "alpha" property to point to a previously defined #ClutterAlpha, e.g.:
111 * <informalexample><programlisting><![CDATA[
113 * "id" : "rotate-behaviour",
114 * "type" : "ClutterBehaviourRotate",
115 * "angle-start" : 0.0,
116 * "angle-end" : 360.0,
119 * "id" : "rotate-alpha",
120 * "type" : "ClutterAlpha",
122 * "id" : "rotate-timeline",
123 * "type : "ClutterTimeline",
127 * "function" : "custom_sine_alpha"
130 * ]]></programlisting></informalexample>
132 * Implicitely defined #ClutterAlpha<!-- -->s and #ClutterTimeline<!-- -->s
133 * can omit the <varname>id</varname> member, as well as the
134 * <varname>type</varname> member, but will not be available using
135 * clutter_script_get_object() (they can, however, be extracted using the
136 * #ClutterBehaviour and #ClutterAlpha API respectively).
138 * Signal handlers can be defined inside a Clutter UI definition file and
139 * then autoconnected to their respective signals using the
140 * clutter_script_connect_signals() function:
142 * <informalexample><programlisting><![CDATA[
145 * { "name" : "button-press-event", "handler" : "on_button_press" },
147 * "name" : "foo-signal",
148 * "handler" : "after_foo",
153 * ]]></programlisting></informalexample>
155 * Signal handler definitions must have a "name" and a "handler" members;
156 * they can also have the "after" and "swapped" boolean members (for the
157 * signal connection flags %G_CONNECT_AFTER and %G_CONNECT_SWAPPED
158 * respectively) and the "object" string member for calling
159 * g_signal_connect_object() instead of g_signal_connect().
161 * Signals can also be directly attached to a specific state defined
162 * inside a #ClutterState instance, for instance:
168 * "name" : "enter-event",
169 * "states" : "button-states",
170 * "target-state" : "hover"
173 * "name" : "leave-event",
174 * "states" : "button-states",
175 * "target-state" : "base"
178 * "name" : "button-press-event",
179 * "states" : "button-states",
180 * "target-state" : "active",
183 * "name" : "key-press-event",
184 * "states" : "button-states",
185 * "target-state" : "key-focus",
192 * The "states" key defines the #ClutterState instance to be used to
193 * resolve the "target-state" key; it can be either a script id for a
194 * #ClutterState built by the same #ClutterScript instance, or to a
195 * #ClutterState built in code and associated to the #ClutterScript
196 * instance through the clutter_script_add_states() function. If no
197 * "states" key is present, then the default #ClutterState associated to
198 * the #ClutterScript instance will be used; the default #ClutterState
199 * can be set using clutter_script_add_states() using a %NULL name. The
200 * "warp" key can be used to warp to a specific state instead of
201 * animating to it. State changes on signal emission will not affect
202 * the signal emission chain.
204 * Clutter reserves the following names, so classes defining properties
205 * through the usual GObject registration process should avoid using these
206 * names to avoid collisions:
208 * <programlisting><![CDATA[
209 * "id" := the unique name of a ClutterScript object
210 * "type" := the class literal name, also used to infer the type
212 * "type_func" := the GType function name, for non-standard classes
213 * "children" := an array of names or objects to add as children
214 * "behaviours" := an array of names or objects to apply to an actor
215 * "signals" := an array of signal definitions to connect to an object
216 * "is-default" := a boolean flag used when defining the #ClutterStage;
217 * if set to "true" the default stage will be used instead
218 * of creating a new #ClutterStage instance
219 * ]]></programlisting>
221 * #ClutterScript is available since Clutter 0.6
234 #include <glib-object.h>
237 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
239 #include "clutter-actor.h"
240 #include "clutter-alpha.h"
241 #include "clutter-stage.h"
242 #include "clutter-texture.h"
244 #include "clutter-script.h"
245 #include "clutter-script-private.h"
246 #include "clutter-scriptable.h"
248 #include "clutter-enum-types.h"
249 #include "clutter-private.h"
250 #include "clutter-debug.h"
252 #include "deprecated/clutter-behaviour.h"
253 #include "deprecated/clutter-container.h"
254 #include "deprecated/clutter-state.h"
262 PROP_TRANSLATION_DOMAIN,
267 static GParamSpec *obj_props[PROP_LAST];
269 #define CLUTTER_SCRIPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_SCRIPT, ClutterScriptPrivate))
271 struct _ClutterScriptPrivate
278 ClutterScriptParser *parser;
282 gchar **search_paths;
284 gchar *translation_domain;
287 guint is_filename : 1;
290 G_DEFINE_TYPE (ClutterScript, clutter_script, G_TYPE_OBJECT);
293 clutter_script_real_get_type_from_name (ClutterScript *script,
294 const gchar *type_name)
298 gtype = g_type_from_name (type_name);
299 if (gtype != G_TYPE_INVALID)
302 return _clutter_script_get_type_from_class (type_name);
306 property_info_free (gpointer data)
310 PropertyInfo *pinfo = data;
313 json_node_free (pinfo->node);
316 g_param_spec_unref (pinfo->pspec);
318 g_free (pinfo->name);
320 g_slice_free (PropertyInfo, pinfo);
325 signal_info_free (gpointer data)
329 SignalInfo *sinfo = data;
331 g_free (sinfo->name);
332 g_free (sinfo->handler);
333 g_free (sinfo->object);
334 g_free (sinfo->state);
335 g_free (sinfo->target);
337 g_slice_free (SignalInfo, sinfo);
342 object_info_free (gpointer data)
346 ObjectInfo *oinfo = data;
349 g_free (oinfo->class_name);
350 g_free (oinfo->type_func);
352 g_list_foreach (oinfo->properties, (GFunc) property_info_free, NULL);
353 g_list_free (oinfo->properties);
355 g_list_foreach (oinfo->signals, (GFunc) signal_info_free, NULL);
356 g_list_free (oinfo->signals);
359 g_list_foreach (oinfo->children, (GFunc) g_free, NULL);
360 g_list_free (oinfo->children);
362 /* we unref top-level objects and leave the actors alone,
363 * unless we are unmerging in which case we have to destroy
364 * the actor to unparent them
366 if (oinfo->object != NULL)
368 if (oinfo->is_unmerged)
370 if (oinfo->is_actor && !oinfo->is_stage)
371 clutter_actor_destroy (CLUTTER_ACTOR (oinfo->object));
374 g_object_unref (oinfo->object);
376 oinfo->object = NULL;
379 g_slice_free (ObjectInfo, oinfo);
384 clutter_script_finalize (GObject *gobject)
386 ClutterScriptPrivate *priv = CLUTTER_SCRIPT_GET_PRIVATE (gobject);
388 g_object_unref (priv->parser);
389 g_hash_table_destroy (priv->objects);
390 g_strfreev (priv->search_paths);
391 g_free (priv->filename);
392 g_hash_table_destroy (priv->states);
393 g_free (priv->translation_domain);
395 G_OBJECT_CLASS (clutter_script_parent_class)->finalize (gobject);
399 clutter_script_set_property (GObject *gobject,
404 ClutterScript *script = CLUTTER_SCRIPT (gobject);
408 case PROP_TRANSLATION_DOMAIN:
409 clutter_script_set_translation_domain (script, g_value_get_string (value));
413 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
419 clutter_script_get_property (GObject *gobject,
424 ClutterScript *script = CLUTTER_SCRIPT (gobject);
428 case PROP_FILENAME_SET:
429 g_value_set_boolean (value, script->priv->is_filename);
433 g_value_set_string (value, script->priv->filename);
436 case PROP_TRANSLATION_DOMAIN:
437 g_value_set_string (value, script->priv->translation_domain);
441 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
447 clutter_script_class_init (ClutterScriptClass *klass)
449 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
451 g_type_class_add_private (klass, sizeof (ClutterScriptPrivate));
453 klass->get_type_from_name = clutter_script_real_get_type_from_name;
456 * ClutterScript:filename-set:
458 * Whether the #ClutterScript:filename property is set. If this property
459 * is %TRUE then the currently parsed data comes from a file, and the
460 * file name is stored inside the #ClutterScript:filename property.
464 obj_props[PROP_FILENAME_SET] =
465 g_param_spec_boolean ("filename-set",
467 P_("Whether the :filename property is set"),
469 CLUTTER_PARAM_READABLE);
472 * ClutterScript:filename:
474 * The path of the currently parsed file. If #ClutterScript:filename-set
475 * is %FALSE then the value of this property is undefined.
479 obj_props[PROP_FILENAME] =
480 g_param_spec_string ("filename",
482 P_("The path of the currently parsed file"),
484 CLUTTER_PARAM_READABLE);
487 * ClutterScript:translation-domain:
489 * The translation domain, used to localize strings marked as translatable
490 * inside a UI definition.
492 * If #ClutterScript:translation-domain is set to %NULL, #ClutterScript
493 * will use gettext(), otherwise g_dgettext() will be used.
497 obj_props[PROP_TRANSLATION_DOMAIN] =
498 g_param_spec_string ("translation-domain",
499 P_("Translation Domain"),
500 P_("The translation domain used to localize string"),
502 CLUTTER_PARAM_READWRITE);
504 gobject_class->set_property = clutter_script_set_property;
505 gobject_class->get_property = clutter_script_get_property;
506 gobject_class->finalize = clutter_script_finalize;
508 g_object_class_install_properties (gobject_class,
514 clutter_script_init (ClutterScript *script)
516 ClutterScriptPrivate *priv;
518 script->priv = priv = CLUTTER_SCRIPT_GET_PRIVATE (script);
520 priv->parser = g_object_new (CLUTTER_TYPE_SCRIPT_PARSER, NULL);
521 priv->parser->script = script;
523 priv->is_filename = FALSE;
524 priv->last_merge_id = 0;
526 priv->objects = g_hash_table_new_full (g_str_hash, g_str_equal,
529 priv->states = g_hash_table_new_full (g_str_hash, g_str_equal,
531 (GDestroyNotify) g_object_unref);
535 * clutter_script_new:
537 * Creates a new #ClutterScript instance. #ClutterScript can be used
538 * to load objects definitions for scenegraph elements, like actors,
539 * or behavioural elements, like behaviours and timelines. The
540 * definitions must be encoded using the JavaScript Object Notation (JSON)
543 * Return value: the newly created #ClutterScript instance. Use
544 * g_object_unref() when done.
549 clutter_script_new (void)
551 return g_object_new (CLUTTER_TYPE_SCRIPT, NULL);
555 * clutter_script_load_from_file:
556 * @script: a #ClutterScript
557 * @filename: the full path to the definition file
558 * @error: return location for a #GError, or %NULL
560 * Loads the definitions from @filename into @script and merges with
561 * the currently loaded ones, if any.
563 * Return value: on error, zero is returned and @error is set
564 * accordingly. On success, the merge id for the UI definitions is
565 * returned. You can use the merge id with clutter_script_unmerge_objects().
570 clutter_script_load_from_file (ClutterScript *script,
571 const gchar *filename,
574 ClutterScriptPrivate *priv;
575 GError *internal_error;
577 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0);
578 g_return_val_if_fail (filename != NULL, 0);
582 g_free (priv->filename);
583 priv->filename = g_strdup (filename);
584 priv->is_filename = TRUE;
585 priv->last_merge_id += 1;
587 internal_error = NULL;
588 json_parser_load_from_file (JSON_PARSER (priv->parser),
593 g_propagate_error (error, internal_error);
594 priv->last_merge_id -= 1;
598 return priv->last_merge_id;
602 * clutter_script_load_from_data:
603 * @script: a #ClutterScript
604 * @data: a buffer containing the definitions
605 * @length: the length of the buffer, or -1 if @data is a NUL-terminated
607 * @error: return location for a #GError, or %NULL
609 * Loads the definitions from @data into @script and merges with
610 * the currently loaded ones, if any.
612 * Return value: on error, zero is returned and @error is set
613 * accordingly. On success, the merge id for the UI definitions is
614 * returned. You can use the merge id with clutter_script_unmerge_objects().
619 clutter_script_load_from_data (ClutterScript *script,
624 ClutterScriptPrivate *priv;
625 GError *internal_error;
627 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0);
628 g_return_val_if_fail (data != NULL, 0);
631 length = strlen (data);
635 g_free (priv->filename);
636 priv->filename = NULL;
637 priv->is_filename = FALSE;
638 priv->last_merge_id += 1;
640 internal_error = NULL;
641 json_parser_load_from_data (JSON_PARSER (priv->parser),
646 g_propagate_error (error, internal_error);
647 priv->last_merge_id -= 1;
651 return priv->last_merge_id;
655 * clutter_script_load_from_resource:
656 * @script: a #ClutterScript
657 * @resource_path: the resource path of the file to parse
658 * @error: return location for a #GError, or %NULL
660 * Loads the definitions from a resource file into @script and merges with
661 * the currently loaded ones, if any.
663 * Return value: on error, zero is returned and @error is set
664 * accordingly. On success, the merge id for the UI definitions is
665 * returned. You can use the merge id with clutter_script_unmerge_objects().
670 clutter_script_load_from_resource (ClutterScript *script,
671 const gchar *resource_path,
677 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0);
679 data = g_resources_lookup_data (resource_path, 0, error);
683 res = clutter_script_load_from_data (script,
684 g_bytes_get_data (data, NULL),
685 g_bytes_get_size (data),
688 g_bytes_unref (data);
694 * clutter_script_get_object:
695 * @script: a #ClutterScript
696 * @name: the name of the object to retrieve
698 * Retrieves the object bound to @name. This function does not increment
699 * the reference count of the returned object.
701 * Return value: (transfer none): the named object, or %NULL if no object
702 * with the given name was available
707 clutter_script_get_object (ClutterScript *script,
712 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL);
713 g_return_val_if_fail (name != NULL, NULL);
715 oinfo = g_hash_table_lookup (script->priv->objects, name);
719 _clutter_script_construct_object (script, oinfo);
720 _clutter_script_apply_properties (script, oinfo);
722 return oinfo->object;
726 clutter_script_get_objects_valist (ClutterScript *script,
727 const gchar *first_name,
736 GObject **obj = NULL;
738 obj = va_arg (args, GObject**);
740 *obj = clutter_script_get_object (script, name);
744 name = va_arg (args, gchar*);
751 * clutter_script_get_objects:
752 * @script: a #ClutterScript
753 * @first_name: the name of the first object to retrieve
754 * @...: return location for a #GObject, then additional names, ending
757 * Retrieves a list of objects for the given names. After @script, object
758 * names/return location pairs should be listed, with a %NULL pointer
759 * ending the list, like:
761 * <informalexample><programlisting>
762 * GObject *my_label, *a_button, *main_timeline;
764 * clutter_script_get_objects (script,
765 * "my-label", &my_label,
766 * "a-button", &a_button,
767 * "main-timeline", &main_timeline,
769 * </programlisting></informalexample>
771 * Note: This function does not increment the reference count of the
774 * Return value: the number of objects returned.
779 clutter_script_get_objects (ClutterScript *script,
780 const gchar *first_name,
786 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0);
787 g_return_val_if_fail (first_name != NULL, 0);
789 va_start (var_args, first_name);
790 retval = clutter_script_get_objects_valist (script, first_name, var_args);
797 ClutterScript *script;
803 remove_by_merge_id (gpointer key,
808 ObjectInfo *oinfo = value;
809 UnmergeData *unmerge_data = data;
811 if (oinfo->merge_id == unmerge_data->merge_id)
813 CLUTTER_NOTE (SCRIPT,
814 "Unmerging object (id:%s, type:%s, merge-id:%d)",
819 unmerge_data->ids = g_slist_prepend (unmerge_data->ids, g_strdup (name));
820 oinfo->is_unmerged = TRUE;
825 * clutter_script_unmerge_objects:
826 * @script: a #ClutterScript
827 * @merge_id: merge id returned when loading a UI definition
829 * Unmerges the objects identified by @merge_id.
834 clutter_script_unmerge_objects (ClutterScript *script,
837 ClutterScriptPrivate *priv;
841 g_return_if_fail (CLUTTER_IS_SCRIPT (script));
842 g_return_if_fail (merge_id > 0);
846 data.script = script;
847 data.merge_id = merge_id;
849 g_hash_table_foreach (priv->objects, remove_by_merge_id, &data);
851 for (l = data.ids; l != NULL; l = l->next)
852 g_hash_table_remove (priv->objects, l->data);
854 g_slist_foreach (data.ids, (GFunc) g_free, NULL);
855 g_slist_free (data.ids);
857 clutter_script_ensure_objects (script);
861 construct_each_objects (gpointer key,
865 ClutterScript *script = user_data;
866 ObjectInfo *oinfo = value;
868 /* we have unfinished business */
869 if (oinfo->has_unresolved)
871 /* this should not happen, but resilence is
872 * a good thing in a parser
874 if (oinfo->object == NULL)
875 _clutter_script_construct_object (script, oinfo);
877 /* this will take care of setting up properties,
878 * adding children and applying behaviours
880 _clutter_script_apply_properties (script, oinfo);
885 * clutter_script_ensure_objects:
886 * @script: a #ClutterScript
888 * Ensure that every object defined inside @script is correctly
889 * constructed. You should rarely need to use this function.
894 clutter_script_ensure_objects (ClutterScript *script)
896 ClutterScriptPrivate *priv;
898 g_return_if_fail (CLUTTER_IS_SCRIPT (script));
901 g_hash_table_foreach (priv->objects, construct_each_objects, script);
905 * clutter_script_get_type_from_name:
906 * @script: a #ClutterScript
907 * @type_name: name of the type to look up
909 * Looks up a type by name, using the virtual function that
910 * #ClutterScript has for that purpose. This function should
913 * Return value: the type for the requested type name, or
914 * %G_TYPE_INVALID if not corresponding type was found.
919 clutter_script_get_type_from_name (ClutterScript *script,
920 const gchar *type_name)
922 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), G_TYPE_INVALID);
923 g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
925 return CLUTTER_SCRIPT_GET_CLASS (script)->get_type_from_name (script, type_name);
929 * clutter_get_script_id:
930 * @gobject: a #GObject
932 * Retrieves the Clutter script id, if any.
934 * Return value: the script id, or %NULL if @object was not defined inside
935 * a UI definition file. The returned string is owned by the object and
936 * should never be modified or freed.
941 clutter_get_script_id (GObject *gobject)
943 g_return_val_if_fail (G_IS_OBJECT (gobject), NULL);
945 if (CLUTTER_IS_SCRIPTABLE (gobject))
946 return clutter_scriptable_get_id (CLUTTER_SCRIPTABLE (gobject));
948 return g_object_get_data (gobject, "clutter-script-id");
956 /* default signal connection code */
958 clutter_script_default_connect (ClutterScript *script,
960 const gchar *signal_name,
961 const gchar *signal_handler,
962 GObject *connect_gobject,
966 ConnectData *data = user_data;
972 if (!g_module_symbol (data->module, signal_handler, (gpointer) &function))
974 g_warning ("Could not find a signal handler '%s' for signal '%s::%s'",
976 connect_gobject ? G_OBJECT_TYPE_NAME (connect_gobject)
977 : G_OBJECT_TYPE_NAME (gobject),
982 CLUTTER_NOTE (SCRIPT,
983 "connecting %s::%s to %s (afetr:%s, swapped:%s, object:%s)",
984 (connect_gobject ? G_OBJECT_TYPE_NAME (connect_gobject)
985 : G_OBJECT_TYPE_NAME (gobject)),
988 (flags & G_CONNECT_AFTER) ? "true" : "false",
989 (flags & G_CONNECT_SWAPPED) ? "true" : "false",
990 (connect_gobject ? G_OBJECT_TYPE_NAME (connect_gobject)
993 if (connect_gobject != NULL)
994 g_signal_connect_object (gobject,
995 signal_name, function,
999 g_signal_connect_data (gobject,
1000 signal_name, function,
1007 * clutter_script_connect_signals:
1008 * @script: a #ClutterScript
1009 * @user_data: data to be passed to the signal handlers, or %NULL
1011 * Connects all the signals defined into a UI definition file to their
1014 * This method invokes clutter_script_connect_signals_full() internally
1015 * and uses #GModule's introspective features (by opening the current
1016 * module's scope) to look at the application's symbol table.
1018 * Note that this function will not work if #GModule is not supported by
1019 * the platform Clutter is running on.
1024 clutter_script_connect_signals (ClutterScript *script,
1029 g_return_if_fail (CLUTTER_IS_SCRIPT (script));
1031 if (!g_module_supported ())
1033 g_critical ("clutter_script_connect_signals() requires a working "
1034 "GModule support from GLib");
1038 cd = g_new (ConnectData, 1);
1039 cd->module = g_module_open (NULL, 0);
1040 cd->data = user_data;
1042 clutter_script_connect_signals_full (script,
1043 clutter_script_default_connect,
1046 g_module_close (cd->module);
1052 ClutterState *state;
1061 ClutterScript *script;
1062 ClutterScriptConnectFunc func;
1064 } SignalConnectData;
1067 hook_data_free (gpointer data)
1069 if (G_LIKELY (data != NULL))
1071 HookData *hook_data = data;
1073 g_free (hook_data->target);
1074 g_slice_free (HookData, hook_data);
1079 clutter_script_state_change_hook (GSignalInvocationHint *ihint,
1081 const GValue *params,
1084 HookData *hook_data = user_data;
1087 emitter = g_value_get_object (¶ms[0]);
1089 if (emitter == hook_data->emitter)
1091 if (hook_data->warp_to)
1092 clutter_state_warp_to_state (hook_data->state, hook_data->target);
1094 clutter_state_set_state (hook_data->state, hook_data->target);
1101 clutter_script_remove_state_change_hook (gpointer user_data,
1104 HookData *hook_data = user_data;
1106 g_signal_remove_emission_hook (hook_data->signal_id,
1107 hook_data->hook_id);
1111 connect_each_object (gpointer key,
1115 SignalConnectData *connect_data = data;
1116 ClutterScript *script = connect_data->script;
1117 ObjectInfo *oinfo = value;
1118 GObject *object = oinfo->object;
1119 GList *unresolved, *l;
1121 _clutter_script_construct_object (script, oinfo);
1124 for (l = oinfo->signals; l != NULL; l = l->next)
1126 SignalInfo *sinfo = l->data;
1128 if (sinfo->is_handler)
1130 GObject *connect_object = NULL;
1133 connect_object = clutter_script_get_object (script, sinfo->object);
1135 if (sinfo->object && !connect_object)
1136 unresolved = g_list_prepend (unresolved, sinfo);
1139 connect_data->func (script, object,
1144 connect_data->user_data);
1149 GObject *state_object = NULL;
1150 const gchar *signal_name, *signal_detail;
1152 GQuark signal_quark;
1154 HookData *hook_data;
1156 if (sinfo->state == NULL)
1157 state_object = (GObject *) clutter_script_get_states (script, NULL);
1160 state_object = clutter_script_get_object (script, sinfo->state);
1161 if (state_object == NULL)
1162 state_object = (GObject *) clutter_script_get_states (script, sinfo->state);
1165 if (state_object == NULL)
1168 components = g_strsplit (sinfo->name, "::", 2);
1169 if (g_strv_length (components) == 2)
1171 signal_name = components[0];
1172 signal_detail = components[1];
1176 signal_name = components[0];
1177 signal_detail = NULL;
1180 signal_id = g_signal_lookup (signal_name, G_OBJECT_TYPE (object));
1183 g_strfreev (components);
1187 if (signal_detail != NULL)
1188 signal_quark = g_quark_from_string (signal_detail);
1192 hook_data = g_slice_new (HookData);
1193 hook_data->emitter = object;
1194 hook_data->state = CLUTTER_STATE (state_object);
1195 hook_data->target = g_strdup (sinfo->target);
1196 hook_data->warp_to = sinfo->warp_to;
1197 hook_data->signal_id = signal_id;
1198 hook_data->hook_id =
1199 g_signal_add_emission_hook (signal_id, signal_quark,
1200 clutter_script_state_change_hook,
1204 g_object_weak_ref (hook_data->emitter,
1205 clutter_script_remove_state_change_hook,
1209 signal_info_free (sinfo);
1212 /* keep the unresolved signal handlers around, in case
1213 * clutter_script_connect_signals() is called multiple
1214 * times (e.g. after a UI definition merge)
1216 g_list_free (oinfo->signals);
1217 oinfo->signals = unresolved;
1221 * clutter_script_connect_signals_full:
1222 * @script: a #ClutterScript
1223 * @func: (scope call): signal connection function
1224 * @user_data: data to be passed to the signal handlers, or %NULL
1226 * Connects all the signals defined into a UI definition file to their
1229 * This function allows to control how the signal handlers are
1230 * going to be connected to their respective signals. It is meant
1231 * primarily for language bindings to allow resolving the function
1232 * names using the native API, but it can also be used on platforms
1233 * that do not support GModule.
1235 * Applications should use clutter_script_connect_signals().
1240 clutter_script_connect_signals_full (ClutterScript *script,
1241 ClutterScriptConnectFunc func,
1244 SignalConnectData data;
1246 g_return_if_fail (CLUTTER_IS_SCRIPT (script));
1247 g_return_if_fail (func != NULL);
1249 data.script = script;
1251 data.user_data = user_data;
1253 g_hash_table_foreach (script->priv->objects, connect_each_object, &data);
1257 clutter_script_error_quark (void)
1259 return g_quark_from_static_string ("clutter-script-error");
1263 * clutter_script_add_search_paths:
1264 * @script: a #ClutterScript
1265 * @paths: (array length=n_paths): an array of strings containing
1266 * different search paths
1267 * @n_paths: the length of the passed array
1269 * Adds @paths to the list of search paths held by @script.
1271 * The search paths are used by clutter_script_lookup_filename(), which
1272 * can be used to define search paths for the textures source file name
1273 * or other custom, file-based properties.
1278 clutter_script_add_search_paths (ClutterScript *script,
1279 const gchar * const paths[],
1282 ClutterScriptPrivate *priv;
1283 gchar **old_paths, **new_paths;
1284 gsize old_paths_len, i;
1287 g_return_if_fail (CLUTTER_IS_SCRIPT (script));
1288 g_return_if_fail (paths != NULL);
1289 g_return_if_fail (n_paths > 0);
1291 priv = script->priv;
1293 if (priv->search_paths)
1295 old_paths = priv->search_paths;
1296 old_paths_len = g_strv_length (old_paths);
1304 new_paths = g_new0 (gchar*, old_paths_len + n_paths + 1);
1306 for (i = 0, iter = 0; i < old_paths_len; i++, iter++)
1307 new_paths[iter] = g_strdup (old_paths[i]);
1309 for (i = 0; i < n_paths; i++, iter++)
1310 new_paths[iter] = g_strdup (paths[i]);
1312 CLUTTER_NOTE (SCRIPT,
1313 "Added %" G_GSIZE_FORMAT " new search paths (new size: %d)",
1315 g_strv_length (new_paths));
1317 priv->search_paths = new_paths;
1320 g_strfreev (old_paths);
1324 * clutter_script_lookup_filename:
1325 * @script: a #ClutterScript
1326 * @filename: the name of the file to lookup
1328 * Looks up @filename inside the search paths of @script. If @filename
1329 * is found, its full path will be returned .
1331 * Return value: the full path of @filename or %NULL if no path was
1337 clutter_script_lookup_filename (ClutterScript *script,
1338 const gchar *filename)
1340 ClutterScriptPrivate *priv;
1344 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL);
1345 g_return_val_if_fail (filename != NULL, NULL);
1347 if (g_path_is_absolute (filename))
1348 return g_strdup (filename);
1350 priv = script->priv;
1352 if (priv->search_paths)
1356 paths_len = g_strv_length (priv->search_paths);
1357 for (i = 0; i < paths_len; i++)
1359 retval = g_build_filename (priv->search_paths[i], filename, NULL);
1360 if (g_file_test (retval, G_FILE_TEST_EXISTS))
1370 /* Fall back to assuming relative to our script */
1371 if (priv->is_filename)
1372 dirname = g_path_get_dirname (script->priv->filename);
1374 dirname = g_get_current_dir ();
1376 retval = g_build_filename (dirname, filename, NULL);
1377 if (!g_file_test (retval, G_FILE_TEST_EXISTS))
1389 * clutter_script_list_objects:
1390 * @script: a #ClutterScript
1392 * Retrieves all the objects created by @script.
1394 * Note: this function does not increment the reference count of the
1395 * objects it returns.
1397 * Return value: (transfer container) (element-type GObject.Object): a list
1398 * of #GObject<!-- -->s, or %NULL. The objects are owned by the
1399 * #ClutterScript instance. Use g_list_free() on the returned list when
1405 clutter_script_list_objects (ClutterScript *script)
1410 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL);
1412 clutter_script_ensure_objects (script);
1413 if (!script->priv->objects)
1417 objects = g_hash_table_get_values (script->priv->objects);
1418 for (l = objects; l != NULL; l = l->next)
1420 ObjectInfo *oinfo = l->data;
1423 retval = g_list_prepend (retval, oinfo->object);
1426 g_list_free (objects);
1432 * clutter_script_add_states:
1433 * @script: a #ClutterScript
1434 * @name: (allow-none): a name for the @state, or %NULL to
1435 * set the default #ClutterState
1436 * @state: a #ClutterState
1438 * Associates a #ClutterState to the #ClutterScript instance using the given
1441 * The #ClutterScript instance will use @state to resolve target states when
1442 * connecting signal handlers.
1444 * The #ClutterScript instance will take a reference on the #ClutterState
1445 * passed to this function.
1452 clutter_script_add_states (ClutterScript *script,
1454 ClutterState *state)
1456 g_return_if_fail (CLUTTER_IS_SCRIPT (script));
1457 g_return_if_fail (CLUTTER_IS_STATE (state));
1459 if (name == NULL || *name == '\0')
1460 name = "__clutter_script_default_state";
1462 g_hash_table_replace (script->priv->states,
1464 g_object_ref (state));
1468 * clutter_script_get_states:
1469 * @script: a #ClutterScript
1470 * @name: (allow-none): the name of the #ClutterState, or %NULL
1472 * Retrieves the #ClutterState for the given @state_name.
1474 * If @name is %NULL, this function will return the default
1475 * #ClutterState instance.
1477 * Return value: (transfer none): a pointer to the #ClutterState for the
1478 * given name. The #ClutterState is owned by the #ClutterScript instance
1479 * and it should not be unreferenced
1486 clutter_script_get_states (ClutterScript *script,
1489 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL);
1491 if (name == NULL || *name == '\0')
1492 name = "__clutter_script_default_state";
1494 return g_hash_table_lookup (script->priv->states, name);
1498 * clutter_script_set_translation_domain:
1499 * @script: a #ClutterScript
1500 * @domain: (allow-none): the translation domain, or %NULL
1502 * Sets the translation domain for @script.
1507 clutter_script_set_translation_domain (ClutterScript *script,
1508 const gchar *domain)
1510 g_return_if_fail (CLUTTER_IS_SCRIPT (script));
1512 if (g_strcmp0 (domain, script->priv->translation_domain) == 0)
1515 g_free (script->priv->translation_domain);
1516 script->priv->translation_domain = g_strdup (domain);
1518 g_object_notify_by_pspec (G_OBJECT (script), obj_props[PROP_TRANSLATION_DOMAIN]);
1522 * clutter_script_get_translation_domain:
1523 * @script: a #ClutterScript
1525 * Retrieves the translation domain set using
1526 * clutter_script_set_translation_domain().
1528 * Return value: (transfer none): the translation domain, if any is set,
1534 clutter_script_get_translation_domain (ClutterScript *script)
1536 g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL);
1538 return script->priv->translation_domain;
1542 * _clutter_script_generate_fake_id:
1543 * @script: a #ClutterScript
1545 * Generates a fake id string for object definitions without
1548 * Return value: a newly-allocated string containing the fake
1549 * id. Use g_free() to free the resources allocated by the
1554 _clutter_script_generate_fake_id (ClutterScript *script)
1556 ClutterScriptPrivate *priv = script->priv;
1558 return g_strdup_printf ("script-%d-%d",
1559 priv->last_merge_id,
1560 priv->last_unknown++);
1564 * _clutter_script_warn_missing_attribute:
1565 * @script: a #ClutterScript
1566 * @id_: the id of an object definition, or %NULL
1567 * @attribute: the expected attribute
1569 * Emits a warning, using GLib's log facilities, for a missing
1570 * @attribute in an object definition, pointing to the current
1571 * location of the #ClutterScriptParser
1574 _clutter_script_warn_missing_attribute (ClutterScript *script,
1576 const gchar *attribute)
1578 ClutterScriptPrivate *priv = script->priv;
1579 JsonParser *parser = JSON_PARSER (priv->parser);
1580 gint current_line = json_parser_get_current_line (parser);
1582 if (id_ != NULL && *id_ != '\0')
1584 g_warning ("%s:%d: object '%s' has no '%s' attribute",
1585 priv->is_filename ? priv->filename : "<input>",
1592 g_warning ("%s:%d: object has no '%s' attribute",
1593 priv->is_filename ? priv->filename : "<input>",
1600 * _clutter_script_warn_invalid_value:
1601 * @script: a #ClutterScript
1602 * @attribute: the attribute with the invalid value
1603 * @expected: a string with the expected value
1604 * @node: a #JsonNode containing the value
1606 * Emits a warning, using GLib's log facilities, for an invalid
1607 * value found when parsing @attribute, pointing to the current
1608 * location of the #ClutterScriptParser
1611 _clutter_script_warn_invalid_value (ClutterScript *script,
1612 const gchar *attribute,
1613 const gchar *expected,
1616 ClutterScriptPrivate *priv = script->priv;
1617 JsonParser *parser = JSON_PARSER (priv->parser);
1618 gint current_line = json_parser_get_current_line (parser);
1622 g_warning ("%s:%d: invalid value of type '%s' for attribute '%s':"
1623 "a value of type '%s' is expected",
1624 priv->is_filename ? priv->filename : "<input>",
1626 json_node_type_name (node),
1632 g_warning ("%s:%d: invalid value for attribute '%s':"
1633 "a value of type '%s' is expected",
1634 priv->is_filename ? priv->filename : "<input>",
1642 * _clutter_script_get_object_info:
1643 * @script: a #ClutterScript
1644 * @script_id: the id of the object definition
1646 * Retrieves the #ObjectInfo for the given @script_id
1648 * Return value: a #ObjectInfo or %NULL
1651 _clutter_script_get_object_info (ClutterScript *script,
1652 const gchar *script_id)
1654 ClutterScriptPrivate *priv = script->priv;
1656 return g_hash_table_lookup (priv->objects, script_id);
1660 * _clutter_script_get_last_merge_id:
1661 * @script: a #ClutterScript
1663 * Retrieves the last merge id of @script. The merge id
1664 * should be stored inside an #ObjectInfo. If you need
1665 * a unique fake id for object definitions with an "id"
1666 * member, consider using _clutter_script_generate_fake_id()
1669 * Return value: the last merge id
1672 _clutter_script_get_last_merge_id (ClutterScript *script)
1674 return script->priv->last_merge_id;
1678 * _clutter_script_add_object_info:
1679 * @script: a #ClutterScript
1680 * @oinfo: a #ObjectInfo
1682 * Adds @oinfo inside the objects list held by @script
1685 _clutter_script_add_object_info (ClutterScript *script,
1688 ClutterScriptPrivate *priv = script->priv;
1690 g_hash_table_steal (priv->objects, oinfo->id);
1691 g_hash_table_insert (priv->objects, oinfo->id, oinfo);