From 7aa52af5dd655282169b72aa5b41b6f6d0853580 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 10 Oct 2007 10:42:19 +0000 Subject: [PATCH] 2007-10-10 Emmanuele Bassi * clutter/json/json-parser.c: Use the commodity JsonNode API and accept bare values as root nodes. * clutter/clutter-script-private.h: * clutter/clutter-script.c: Unreference the created objects only if they are top-levels, like ClutterBehaviour and ClutterTimelines. Actors have floating references, so we just transfer ownership to their containers, and the stage is owned by the backend. Add the "type_func" key to the object definition, so the user can supply its own GType function if the class name doesn't follow the GObject rules. Document the ClutterScript public API. --- ChangeLog | 15 ++++ clutter/clutter-script-private.h | 5 +- clutter/clutter-script.c | 130 ++++++++++++++++++++++++++++++- clutter/json/json-parser.c | 79 +++++++------------ doc/reference/ChangeLog | 6 ++ doc/reference/Makefile.am | 1 + doc/reference/clutter.types | 1 + 7 files changed, 183 insertions(+), 54 deletions(-) diff --git a/ChangeLog b/ChangeLog index 855b46f95..a5f79fd7c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2007-10-10 Emmanuele Bassi + + * clutter/json/json-parser.c: Use the commodity JsonNode API + and accept bare values as root nodes. + + * clutter/clutter-script-private.h: + * clutter/clutter-script.c: Unreference the created objects + only if they are top-levels, like ClutterBehaviour and + ClutterTimelines. Actors have floating references, so we + just transfer ownership to their containers, and the stage + is owned by the backend. Add the "type_func" key to the + object definition, so the user can supply its own GType + function if the class name doesn't follow the GObject rules. + Document the ClutterScript public API. + 2007-10-10 Matthew Allum * clutter/clutter-actor.c: diff --git a/clutter/clutter-script-private.h b/clutter/clutter-script-private.h index a8a86378d..93eeb5030 100644 --- a/clutter/clutter-script-private.h +++ b/clutter/clutter-script-private.h @@ -34,8 +34,9 @@ G_BEGIN_DECLS typedef GType (* GTypeGetFunc) (void); typedef struct { - gchar *class_name; gchar *id; + gchar *class_name; + gchar *type_func; GList *properties; GList *children; @@ -43,6 +44,8 @@ typedef struct { GType gtype; GObject *object; + + guint is_toplevel : 1; } ObjectInfo; typedef struct { diff --git a/clutter/clutter-script.c b/clutter/clutter-script.c index 1f11825fe..8e60bdd4d 100644 --- a/clutter/clutter-script.c +++ b/clutter/clutter-script.c @@ -83,6 +83,18 @@ * } * * + * And then to apply a defined behaviour to an actor defined inside the + * definition of an actor, the "behaviour" member can be used: + * + * + * { + * "id" : "my-rotating-actor", + * "type" : "ClutterTexture", + * ... + * "behaviours" : [ "rotate-behaviour" ] + * } + * + * * #ClutterScript is available since Clutter 0.6 */ @@ -130,6 +142,22 @@ G_DEFINE_TYPE (ClutterScript, clutter_script, G_TYPE_OBJECT); static void object_info_free (gpointer data); +static GType +resolve_type (const gchar *symbol) +{ + static GModule *module = NULL; + GTypeGetFunc func; + GType gtype = G_TYPE_INVALID; + + if (!module) + module = g_module_open (NULL, 0); + + if (g_module_symbol (module, symbol, (gpointer)&func)) + gtype = func (); + + return gtype; +} + /* tries to map a name in camel case into the corresponding get_type() * function, e.g.: * @@ -306,6 +334,7 @@ construct_timeline (ClutterScript *script, retval = CLUTTER_TIMELINE (clutter_script_construct_object (script, oinfo)); + /* it needs to survive */ g_object_ref (retval); object_info_free (oinfo); @@ -536,6 +565,7 @@ json_object_end (JsonParser *parser, JsonNode *val; GList *members, *l; + /* ignore any non-named object */ if (!json_object_has_member (object, "id")) return; @@ -555,6 +585,14 @@ json_object_end (JsonParser *parser, val = json_object_get_member (object, "type"); oinfo->class_name = json_node_dup_string (val); + if (json_object_has_member (object, "type_func")) + { + val = json_object_get_member (object, "type_func"); + oinfo->type_func = json_node_dup_string (val); + } + + oinfo->is_toplevel = FALSE; + members = json_object_get_members (object); for (l = members; l; l = l->next) { @@ -708,6 +746,9 @@ translate_property (ClutterScript *script, retval = TRUE; break; case G_TYPE_ENUM: + /* enumeration values can be expressed using the nick field + * of GEnumValue or the actual integer value + */ if (G_VALUE_HOLDS (src, G_TYPE_STRING)) { const gchar *string = g_value_get_string (src); @@ -736,6 +777,9 @@ translate_property (ClutterScript *script, return retval; } +/* translates the PropertyInfo structure into a GParameter array to + * be fed to g_object_newv() + */ static void translate_properties (ClutterScript *script, ObjectInfo *oinfo, @@ -923,7 +967,11 @@ clutter_script_construct_object (ClutterScript *script, if (oinfo->gtype == G_TYPE_INVALID) { - oinfo->gtype = resolve_type_lazily (oinfo->class_name); + if (oinfo->type_func) + oinfo->gtype = resolve_type (oinfo->type_func); + else + oinfo->gtype = resolve_type_lazily (oinfo->class_name); + if (oinfo->gtype == G_TYPE_INVALID) return NULL; } @@ -958,6 +1006,10 @@ clutter_script_construct_object (ClutterScript *script, if (CLUTTER_IS_ACTOR (oinfo->object) && oinfo->behaviours) apply_behaviours (script, CLUTTER_ACTOR (oinfo->object), oinfo->behaviours); + if (CLUTTER_IS_BEHAVIOUR (oinfo->object) || + CLUTTER_IS_TIMELINE (oinfo->object)) + oinfo->is_toplevel = TRUE; + if (oinfo->id) g_object_set_data_full (oinfo->object, "clutter-script-name", g_strdup (oinfo->id), @@ -995,8 +1047,9 @@ object_info_free (gpointer data) ObjectInfo *oinfo = data; GList *l; - g_free (oinfo->class_name); g_free (oinfo->id); + g_free (oinfo->class_name); + g_free (oinfo->type_func); for (l = oinfo->properties; l; l = l->next) { @@ -1014,7 +1067,7 @@ object_info_free (gpointer data) g_list_foreach (oinfo->behaviours, (GFunc) g_free, NULL); g_list_free (oinfo->behaviours); - if (oinfo->object) + if (oinfo->is_toplevel && oinfo->object) g_object_unref (oinfo->object); g_slice_free (ObjectInfo, oinfo); @@ -1066,12 +1119,40 @@ clutter_script_init (ClutterScript *script) object_info_free); } +/** + * clutter_script_new: + * + * Creates a new #ClutterScript instance. #ClutterScript can be used + * to load objects definitions for scenegraph elements, like actors, + * or behavioural elements, like behaviours and timelines. The + * definitions must be encoded using the JavaScript Object Notation (JSON) + * language. + * + * Return value: the newly created #ClutterScript instance. Use + * g_object_unref() when done. + * + * Since: 0.6 + */ ClutterScript * clutter_script_new (void) { return g_object_new (CLUTTER_TYPE_SCRIPT, NULL); } +/** + * clutter_script_load_from_file: + * @script: a #ClutterScript + * @filename: the full path to the definition file + * @error: return location for a #GError, or %NULL + * + * Loads the definitions from @filename into @script and merges with + * the currently loaded ones, if any. + * + * Return value: on error, zero is returned and @error is set + * accordingly. On success, a positive integer is returned. + * + * Since: 0.6 + */ guint clutter_script_load_from_file (ClutterScript *script, const gchar *filename, @@ -1102,6 +1183,22 @@ clutter_script_load_from_file (ClutterScript *script, return priv->last_merge_id; } +/** + * clutter_script_load_from_data: + * @script: a #ClutterScript + * @data: a buffer containing the definitions + * @length: the length of the buffer, or -1 if @data is a NUL-terminated + * buffer + * @error: return location for a #GError, or %NULL + * + * Loads the definitions from @data into @script and merges with + * the currently loaded ones, if any. + * + * Return value: on error, zero is returned and @error is set + * accordingly. On success, a positive integer is returned. + * + * Since: 0.6 + */ guint clutter_script_load_from_data (ClutterScript *script, const gchar *data, @@ -1133,6 +1230,19 @@ clutter_script_load_from_data (ClutterScript *script, return priv->last_merge_id; } +/** + * clutter_script_get_object: + * @script: a #ClutterScript + * @name: the name of the object to retrieve + * + * Retrieves the object bound to @name. This function does not increment + * the reference count of the returned object. + * + * Return value: the named object, or %NULL if no object with the + * given name was available + * + * Since: 0.6 + */ GObject * clutter_script_get_object (ClutterScript *script, const gchar *name) @@ -1169,6 +1279,20 @@ clutter_script_get_objects_valist (ClutterScript *script, return g_list_reverse (retval); } +/** + * clutter_script_get_objects: + * @script: a #ClutterScript + * @first_name: the name of the first object to retrieve + * @Varargs: a %NULL-terminated list of names + * + * Retrieves a list of objects for the given names. This function does + * not increment the reference count of the returned objects. + * + * Return value: a newly allocated #GList containing the found objects, + * or %NULL. Use g_list_free() when done using it. + * + * Since: 0.6 + */ GList * clutter_script_get_objects (ClutterScript *script, const gchar *first_name, diff --git a/clutter/json/json-parser.c b/clutter/json/json-parser.c index 2f9e24c26..19ff5c81a 100644 --- a/clutter/json/json-parser.c +++ b/clutter/json/json-parser.c @@ -343,7 +343,6 @@ json_parse_array (JsonParser *parser, while (token != G_TOKEN_RIGHT_BRACE) { JsonNode *node = NULL; - GValue value = { 0, }; if (token == G_TOKEN_COMMA) { @@ -421,44 +420,25 @@ json_parse_array (JsonParser *parser, switch (token) { case G_TOKEN_INT: - g_value_init (&value, G_TYPE_INT); - g_value_set_int (&value, scanner->value.v_int); - node = json_node_new (JSON_NODE_VALUE); - json_node_set_value (node, &value); - - g_value_unset (&value); + json_node_set_int (node, scanner->value.v_int); break; case G_TOKEN_FLOAT: - g_value_init (&value, G_TYPE_DOUBLE); - g_value_set_double (&value, scanner->value.v_float); - node = json_node_new (JSON_NODE_VALUE); - json_node_set_value (node, &value); - - g_value_unset (&value); + json_node_set_double (node, scanner->value.v_float); break; case G_TOKEN_STRING: - g_value_init (&value, G_TYPE_STRING); - g_value_set_string (&value, scanner->value.v_string); - node = json_node_new (JSON_NODE_VALUE); - json_node_set_value (node, &value); - - g_value_unset (&value); + json_node_set_string (node, scanner->value.v_string); break; case JSON_TOKEN_TRUE: case JSON_TOKEN_FALSE: - g_value_init (&value, G_TYPE_BOOLEAN); - g_value_set_boolean (&value, token == JSON_TOKEN_TRUE ? TRUE : FALSE); - node = json_node_new (JSON_NODE_VALUE); - json_node_set_value (node, &value); - - g_value_unset (&value); + json_node_set_boolean (node, token == JSON_TOKEN_TRUE ? TRUE + : FALSE); break; case JSON_TOKEN_NULL: @@ -515,7 +495,6 @@ json_parse_object (JsonParser *parser, { JsonNode *node = NULL; gchar *name = NULL; - GValue value = { 0, }; if (token == G_TOKEN_COMMA) { @@ -621,44 +600,25 @@ json_parse_object (JsonParser *parser, switch (token) { case G_TOKEN_INT: - g_value_init (&value, G_TYPE_INT); - g_value_set_int (&value, scanner->value.v_int); - node = json_node_new (JSON_NODE_VALUE); - json_node_set_value (node, &value); - - g_value_unset (&value); + json_node_set_int (node, scanner->value.v_int); break; case G_TOKEN_FLOAT: - g_value_init (&value, G_TYPE_DOUBLE); - g_value_set_double (&value, scanner->value.v_float); - node = json_node_new (JSON_NODE_VALUE); - json_node_set_value (node, &value); - - g_value_unset (&value); + json_node_set_double (node, scanner->value.v_float); break; case G_TOKEN_STRING: - g_value_init (&value, G_TYPE_STRING); - g_value_set_string (&value, scanner->value.v_string); - node = json_node_new (JSON_NODE_VALUE); - json_node_set_value (node, &value); - - g_value_unset (&value); + json_node_set_string (node, scanner->value.v_string); break; case JSON_TOKEN_TRUE: case JSON_TOKEN_FALSE: - g_value_init (&value, G_TYPE_BOOLEAN); - g_value_set_boolean (&value, token == JSON_TOKEN_TRUE ? TRUE : FALSE); - node = json_node_new (JSON_NODE_VALUE); - json_node_set_value (node, &value); - - g_value_unset (&value); + json_node_set_boolean (node, token == JSON_TOKEN_TRUE ? TRUE + : FALSE); break; case JSON_TOKEN_NULL: @@ -713,6 +673,25 @@ json_parse_statement (JsonParser *parser, priv->root = priv->current_node = json_node_new (JSON_NODE_NULL); return G_TOKEN_NONE; + case JSON_TOKEN_TRUE: + case JSON_TOKEN_FALSE: + priv->root = priv->current_node = json_node_new (JSON_NODE_VALUE); + json_node_set_boolean (priv->current_node, + token == JSON_TOKEN_TRUE ? TRUE : FALSE); + return G_TOKEN_NONE; + + case G_TOKEN_INT: + case G_TOKEN_FLOAT: + case G_TOKEN_STRING: + priv->root = priv->current_node = json_node_new (JSON_NODE_VALUE); + if (token == G_TOKEN_INT) + json_node_set_int (priv->current_node, scanner->value.v_int); + else if (token == G_TOKEN_FLOAT) + json_node_set_double (priv->current_node, scanner->value.v_float); + else + json_node_set_string (priv->current_node, scanner->value.v_string); + return G_TOKEN_NONE; + default: g_scanner_get_next_token (scanner); return G_TOKEN_SYMBOL; diff --git a/doc/reference/ChangeLog b/doc/reference/ChangeLog index 4ebfd3179..c9846e21e 100644 --- a/doc/reference/ChangeLog +++ b/doc/reference/ChangeLog @@ -1,3 +1,9 @@ +2007-10-10 Emmanuele Bassi + + * clutter.types: + * Makefile.am: Add ClutterScript and ignore clutter-script-private.h + to avoid picking up private symbols. + 2007-08-21 Emmanuele Bassi * clutter-sections.txt: Move ClutterStage and ClutterStageClass diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am index 7380da81a..b5ce1b8ff 100644 --- a/doc/reference/Makefile.am +++ b/doc/reference/Makefile.am @@ -54,6 +54,7 @@ IGNORE_HFILES=\ clutter-keysyms.h \ clutter-keysyms-table.h \ clutter-enum-types.h \ + clutter-script-private.h \ stamp-clutter-enum-types.h \ stamp-clutter-marshal.h \ cogl \ diff --git a/doc/reference/clutter.types b/doc/reference/clutter.types index 1ebae44a0..8a2d4b46a 100644 --- a/doc/reference/clutter.types +++ b/doc/reference/clutter.types @@ -24,3 +24,4 @@ clutter_entry_get_type clutter_box_get_type clutter_hbox_get_type clutter_vbox_get_type +clutter_script_get_type