2009-08-12 09:30:11 +00:00
|
|
|
/*
|
|
|
|
* Clutter.
|
|
|
|
*
|
|
|
|
* An OpenGL based 'interactive canvas' library.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
|
|
|
|
* Copyright (C) 2009 Intel Corportation
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2010-03-01 12:56:10 +00:00
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
2009-08-12 09:30:11 +00:00
|
|
|
* Original author:
|
|
|
|
*
|
|
|
|
* Emmanuele Bassi <ebassi@linux.intel.com>
|
|
|
|
*/
|
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <glib.h>
|
2008-04-25 13:37:36 +00:00
|
|
|
#include <gmodule.h>
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
#include "clutter-actor.h"
|
2012-08-23 16:37:40 +00:00
|
|
|
#include "clutter-container.h"
|
2009-11-04 13:32:26 +00:00
|
|
|
#include "clutter-debug.h"
|
|
|
|
#include "clutter-enum-types.h"
|
2012-08-23 16:37:40 +00:00
|
|
|
#include "clutter-private.h"
|
2007-10-25 14:34:54 +00:00
|
|
|
#include "clutter-script.h"
|
|
|
|
#include "clutter-script-private.h"
|
|
|
|
#include "clutter-scriptable.h"
|
2011-11-09 17:09:37 +00:00
|
|
|
#include "clutter-stage-manager.h"
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
static void clutter_script_parser_object_end (JsonParser *parser,
|
|
|
|
JsonObject *object);
|
|
|
|
static void clutter_script_parser_parse_end (JsonParser *parser);
|
|
|
|
|
2012-02-09 18:40:03 +00:00
|
|
|
#define clutter_script_parser_get_type _clutter_script_parser_get_type
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
G_DEFINE_TYPE (ClutterScriptParser, clutter_script_parser, JSON_TYPE_PARSER);
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_script_parser_class_init (ClutterScriptParserClass *klass)
|
|
|
|
{
|
|
|
|
JsonParserClass *parser_class = JSON_PARSER_CLASS (klass);
|
|
|
|
|
|
|
|
parser_class->object_end = clutter_script_parser_object_end;
|
|
|
|
parser_class->parse_end = clutter_script_parser_parse_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_script_parser_init (ClutterScriptParser *parser)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
GType
|
2011-09-07 15:14:10 +00:00
|
|
|
_clutter_script_get_type_from_symbol (const gchar *symbol)
|
2007-10-25 14:34:54 +00:00
|
|
|
{
|
|
|
|
static GModule *module = NULL;
|
|
|
|
GTypeGetFunc func;
|
|
|
|
GType gtype = G_TYPE_INVALID;
|
|
|
|
|
|
|
|
if (!module)
|
2009-11-06 14:04:36 +00:00
|
|
|
module = g_module_open (NULL, 0);
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
if (g_module_symbol (module, symbol, (gpointer)&func))
|
|
|
|
gtype = func ();
|
|
|
|
|
|
|
|
return gtype;
|
|
|
|
}
|
|
|
|
|
|
|
|
GType
|
2011-09-07 15:14:10 +00:00
|
|
|
_clutter_script_get_type_from_class (const gchar *name)
|
2007-10-25 14:34:54 +00:00
|
|
|
{
|
|
|
|
static GModule *module = NULL;
|
2008-08-04 16:21:27 +00:00
|
|
|
GString *symbol_name = g_string_sized_new (64);
|
2007-10-25 14:34:54 +00:00
|
|
|
GType gtype = G_TYPE_INVALID;
|
2008-08-04 16:21:27 +00:00
|
|
|
GTypeGetFunc func;
|
|
|
|
gchar *symbol;
|
|
|
|
gint i;
|
2007-10-25 14:34:54 +00:00
|
|
|
|
2008-08-04 16:21:27 +00:00
|
|
|
if (G_UNLIKELY (!module))
|
2009-11-06 14:04:36 +00:00
|
|
|
module = g_module_open (NULL, 0);
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
for (i = 0; name[i] != '\0'; i++)
|
|
|
|
{
|
2008-08-04 16:21:27 +00:00
|
|
|
gchar c = name[i];
|
|
|
|
|
|
|
|
/* the standard naming policy for GObject-based libraries
|
|
|
|
* is:
|
|
|
|
*
|
|
|
|
* NAME := INITIAL_WORD WORD+
|
|
|
|
* INITIAL_WORD := [A-Z][a-z0-9]*
|
|
|
|
* WORD := [A-Z]{1,2}[a-z0-9]+ | [A-Z]{2,}
|
|
|
|
*
|
|
|
|
* for instance:
|
|
|
|
*
|
|
|
|
* GString -> g_string
|
|
|
|
* GtkCTree -> gtk_ctree
|
|
|
|
* ClutterX11TexturePixmap -> clutter_x11_texture_pixmap
|
|
|
|
*
|
|
|
|
* see:
|
|
|
|
*
|
|
|
|
* http://mail.gnome.org/archives/gtk-devel-list/2007-June/msg00022.html
|
2009-08-12 09:30:11 +00:00
|
|
|
*
|
|
|
|
* and:
|
|
|
|
*
|
|
|
|
* http://git.gnome.org/cgit/gtk+/plain/gtk/gtkbuilderparser.c
|
2008-08-04 16:21:27 +00:00
|
|
|
*/
|
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
if ((c == g_ascii_toupper (c) &&
|
2008-08-04 16:21:27 +00:00
|
|
|
i > 0 && name[i - 1] != g_ascii_toupper (name[i - 1])) ||
|
|
|
|
(i > 2 && name[i] == g_ascii_toupper (name[i]) &&
|
|
|
|
name[i - 1] == g_ascii_toupper (name[i - 1]) &&
|
|
|
|
name[i - 2] == g_ascii_toupper (name[i - 2])))
|
2007-10-25 14:34:54 +00:00
|
|
|
g_string_append_c (symbol_name, '_');
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
g_string_append_c (symbol_name, g_ascii_tolower (c));
|
|
|
|
}
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
g_string_append (symbol_name, "_get_type");
|
|
|
|
|
|
|
|
symbol = g_string_free (symbol_name, FALSE);
|
|
|
|
|
|
|
|
if (g_module_symbol (module, symbol, (gpointer)&func))
|
2008-02-07 14:17:53 +00:00
|
|
|
{
|
|
|
|
CLUTTER_NOTE (SCRIPT, "Type function: %s", symbol);
|
|
|
|
gtype = func ();
|
|
|
|
}
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
g_free (symbol);
|
|
|
|
|
|
|
|
return gtype;
|
|
|
|
}
|
|
|
|
|
2008-08-04 16:21:27 +00:00
|
|
|
/*
|
|
|
|
* clutter_script_enum_from_string:
|
|
|
|
* @type: a #GType for an enumeration type
|
|
|
|
* @string: the enumeration value as a string
|
2010-09-08 14:37:58 +00:00
|
|
|
* @enum_value: (out): return location for the enumeration value as an integer
|
2008-08-04 16:21:27 +00:00
|
|
|
*
|
|
|
|
* Converts an enumeration value inside @string into a numeric
|
|
|
|
* value and places it into @enum_value.
|
|
|
|
*
|
|
|
|
* The enumeration value can be an integer, the enumeration nick
|
|
|
|
* or the enumeration name, as part of the #GEnumValue structure.
|
|
|
|
*
|
|
|
|
* Return value: %TRUE if the conversion was successfull.
|
|
|
|
*/
|
2007-10-25 14:34:54 +00:00
|
|
|
gboolean
|
2011-09-07 15:14:10 +00:00
|
|
|
_clutter_script_enum_from_string (GType type,
|
|
|
|
const gchar *string,
|
|
|
|
gint *enum_value)
|
2007-10-25 14:34:54 +00:00
|
|
|
{
|
|
|
|
GEnumClass *eclass;
|
|
|
|
GEnumValue *ev;
|
|
|
|
gchar *endptr;
|
|
|
|
gint value;
|
|
|
|
gboolean retval = TRUE;
|
|
|
|
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_ENUM (type), 0);
|
|
|
|
g_return_val_if_fail (string != NULL, 0);
|
|
|
|
|
|
|
|
value = strtoul (string, &endptr, 0);
|
|
|
|
if (endptr != string) /* parsed a number */
|
|
|
|
*enum_value = value;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eclass = g_type_class_ref (type);
|
|
|
|
ev = g_enum_get_value_by_name (eclass, string);
|
|
|
|
if (!ev)
|
|
|
|
ev = g_enum_get_value_by_nick (eclass, string);
|
|
|
|
|
|
|
|
if (ev)
|
|
|
|
*enum_value = ev->value;
|
|
|
|
else
|
|
|
|
retval = FALSE;
|
|
|
|
|
|
|
|
g_type_class_unref (eclass);
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
2011-09-07 15:14:10 +00:00
|
|
|
_clutter_script_flags_from_string (GType type,
|
|
|
|
const gchar *string,
|
|
|
|
gint *flags_value)
|
2007-10-25 14:34:54 +00:00
|
|
|
{
|
|
|
|
gchar *endptr, *prevptr;
|
|
|
|
guint i, j, ret, value;
|
|
|
|
gchar *flagstr;
|
|
|
|
GFlagsValue *fv;
|
|
|
|
const gchar *flag;
|
|
|
|
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_FLAGS (type), 0);
|
2010-05-27 07:28:29 +00:00
|
|
|
g_return_val_if_fail (string != NULL, 0);
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
value = strtoul (string, &endptr, 0);
|
|
|
|
if (endptr != string) /* parsed a number */
|
|
|
|
*flags_value = value;
|
|
|
|
else
|
|
|
|
{
|
2008-08-04 16:21:27 +00:00
|
|
|
GFlagsClass *fclass;
|
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
fclass = g_type_class_ref (type);
|
|
|
|
|
|
|
|
flagstr = g_strdup (string);
|
|
|
|
for (value = i = j = 0; ; i++)
|
|
|
|
{
|
2008-08-04 16:21:27 +00:00
|
|
|
gboolean eos = (flagstr[i] == '\0') ? TRUE : FALSE;
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
if (!eos && flagstr[i] != '|')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
flag = &flagstr[j];
|
|
|
|
endptr = &flagstr[i];
|
|
|
|
|
|
|
|
if (!eos)
|
|
|
|
{
|
|
|
|
flagstr[i++] = '\0';
|
|
|
|
j = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* trim spaces */
|
|
|
|
for (;;)
|
|
|
|
{
|
2008-08-04 16:21:27 +00:00
|
|
|
gunichar ch = g_utf8_get_char (flag);
|
2007-10-25 14:34:54 +00:00
|
|
|
if (!g_unichar_isspace (ch))
|
|
|
|
break;
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
flag = g_utf8_next_char (flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (endptr > flag)
|
|
|
|
{
|
2008-08-04 16:21:27 +00:00
|
|
|
gunichar ch;
|
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
prevptr = g_utf8_prev_char (endptr);
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
ch = g_utf8_get_char (prevptr);
|
|
|
|
if (!g_unichar_isspace (ch))
|
|
|
|
break;
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
endptr = prevptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (endptr > flag)
|
|
|
|
{
|
|
|
|
*endptr = '\0';
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2007-10-25 14:34:54 +00:00
|
|
|
fv = g_flags_get_value_by_name (fclass, flag);
|
|
|
|
|
|
|
|
if (!fv)
|
|
|
|
fv = g_flags_get_value_by_nick (fclass, flag);
|
|
|
|
|
|
|
|
if (fv)
|
|
|
|
value |= fv->value;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eos)
|
|
|
|
{
|
|
|
|
*flags_value = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (flagstr);
|
|
|
|
|
|
|
|
g_type_class_unref (fclass);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
parse_knot_from_array (JsonArray *array,
|
|
|
|
ClutterKnot *knot)
|
|
|
|
{
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_array_get_length (array) != 2)
|
2007-10-25 14:34:54 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
knot->x = json_array_get_int_element (array, 0);
|
|
|
|
knot->y = json_array_get_int_element (array, 1);
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
parse_knot_from_object (JsonObject *object,
|
|
|
|
ClutterKnot *knot)
|
|
|
|
{
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "x"))
|
|
|
|
knot->x = json_object_get_int_member (object, "x");
|
|
|
|
else
|
|
|
|
knot->x = 0;
|
2007-10-25 14:34:54 +00:00
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "y"))
|
|
|
|
knot->y = json_object_get_int_member (object, "y");
|
|
|
|
else
|
|
|
|
knot->y = 0;
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
2011-09-07 15:14:10 +00:00
|
|
|
_clutter_script_parse_knot (ClutterScript *script,
|
|
|
|
JsonNode *node,
|
|
|
|
ClutterKnot *knot)
|
2007-10-25 14:34:54 +00:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
|
|
|
|
g_return_val_if_fail (node != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (knot != NULL, FALSE);
|
|
|
|
|
|
|
|
switch (JSON_NODE_TYPE (node))
|
|
|
|
{
|
|
|
|
case JSON_NODE_ARRAY:
|
|
|
|
return parse_knot_from_array (json_node_get_array (node), knot);
|
|
|
|
|
|
|
|
case JSON_NODE_OBJECT:
|
|
|
|
return parse_knot_from_object (json_node_get_object (node), knot);
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2013-03-30 00:24:17 +00:00
|
|
|
parse_rect_from_array (JsonArray *array,
|
|
|
|
ClutterRect *rect)
|
2007-10-25 14:34:54 +00:00
|
|
|
{
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_array_get_length (array) != 4)
|
2007-10-25 14:34:54 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2013-03-30 00:24:17 +00:00
|
|
|
rect->origin.x = json_array_get_double_element (array, 0);
|
|
|
|
rect->origin.y = json_array_get_double_element (array, 1);
|
|
|
|
rect->size.width = json_array_get_double_element (array, 2);
|
|
|
|
rect->size.height = json_array_get_double_element (array, 3);
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2013-03-30 00:24:17 +00:00
|
|
|
parse_rect_from_object (JsonObject *object,
|
|
|
|
ClutterRect *rect)
|
2007-10-25 14:34:54 +00:00
|
|
|
{
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "x"))
|
2013-03-30 00:24:17 +00:00
|
|
|
rect->origin.x = json_object_get_double_member (object, "x");
|
2009-11-04 15:20:12 +00:00
|
|
|
else
|
2013-03-30 00:24:17 +00:00
|
|
|
rect->origin.x = 0;
|
2007-10-25 14:34:54 +00:00
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "y"))
|
2013-03-30 00:24:17 +00:00
|
|
|
rect->origin.y = json_object_get_double_member (object, "y");
|
2009-11-04 15:20:12 +00:00
|
|
|
else
|
2013-03-30 00:24:17 +00:00
|
|
|
rect->origin.y = 0;
|
2007-10-25 14:34:54 +00:00
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "width"))
|
2013-03-30 00:24:17 +00:00
|
|
|
rect->size.width = json_object_get_double_member (object, "width");
|
2009-11-04 15:20:12 +00:00
|
|
|
else
|
2013-03-30 00:24:17 +00:00
|
|
|
rect->size.width = 0;
|
2007-10-25 14:34:54 +00:00
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "height"))
|
2013-03-30 00:24:17 +00:00
|
|
|
rect->size.height = json_object_get_double_member (object, "height");
|
2009-11-04 15:20:12 +00:00
|
|
|
else
|
2013-03-30 00:24:17 +00:00
|
|
|
rect->size.height = 0;
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
2013-03-30 00:24:17 +00:00
|
|
|
_clutter_script_parse_rect (ClutterScript *script,
|
|
|
|
JsonNode *node,
|
|
|
|
ClutterRect *rect)
|
2007-10-25 14:34:54 +00:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
|
|
|
|
g_return_val_if_fail (node != NULL, FALSE);
|
2013-03-30 00:24:17 +00:00
|
|
|
g_return_val_if_fail (rect != NULL, FALSE);
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
switch (JSON_NODE_TYPE (node))
|
|
|
|
{
|
|
|
|
case JSON_NODE_ARRAY:
|
2013-03-30 00:24:17 +00:00
|
|
|
return parse_rect_from_array (json_node_get_array (node), rect);
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
case JSON_NODE_OBJECT:
|
2013-03-30 00:24:17 +00:00
|
|
|
return parse_rect_from_object (json_node_get_object (node), rect);
|
2007-10-25 14:34:54 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
2008-08-04 16:21:27 +00:00
|
|
|
|
|
|
|
static gboolean
|
|
|
|
parse_color_from_array (JsonArray *array,
|
|
|
|
ClutterColor *color)
|
|
|
|
{
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_array_get_length (array) != 3 ||
|
|
|
|
json_array_get_length (array) != 4)
|
2008-08-04 16:21:27 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
color->red = CLAMP (json_array_get_int_element (array, 0), 0, 255);
|
|
|
|
color->green = CLAMP (json_array_get_int_element (array, 1), 0, 255);
|
|
|
|
color->blue = CLAMP (json_array_get_int_element (array, 2), 0, 255);
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_array_get_length (array) == 4)
|
|
|
|
color->alpha = CLAMP (json_array_get_int_element (array, 3), 0, 255);
|
|
|
|
else
|
|
|
|
color->alpha = 255;
|
2008-08-04 16:21:27 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
parse_color_from_object (JsonObject *object,
|
|
|
|
ClutterColor *color)
|
|
|
|
{
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "red"))
|
|
|
|
color->red = CLAMP (json_object_get_int_member (object, "red"), 0, 255);
|
|
|
|
else
|
|
|
|
color->red = 0;
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "green"))
|
|
|
|
color->green = CLAMP (json_object_get_int_member (object, "green"), 0, 255);
|
|
|
|
else
|
|
|
|
color->green = 0;
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "blue"))
|
|
|
|
color->blue = CLAMP (json_object_get_int_member (object, "blue"), 0, 255);
|
|
|
|
else
|
|
|
|
color->blue = 0;
|
2008-08-04 16:21:27 +00:00
|
|
|
|
2009-11-04 15:20:12 +00:00
|
|
|
if (json_object_has_member (object, "alpha"))
|
|
|
|
color->alpha = CLAMP (json_object_get_int_member (object, "alpha"), 0, 255);
|
|
|
|
else
|
|
|
|
color->alpha = 255;
|
2008-08-04 16:21:27 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
2011-09-07 15:14:10 +00:00
|
|
|
_clutter_script_parse_color (ClutterScript *script,
|
|
|
|
JsonNode *node,
|
|
|
|
ClutterColor *color)
|
2008-08-04 16:21:27 +00:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
|
|
|
|
g_return_val_if_fail (node != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (color != NULL, FALSE);
|
|
|
|
|
|
|
|
switch (JSON_NODE_TYPE (node))
|
|
|
|
{
|
|
|
|
case JSON_NODE_ARRAY:
|
|
|
|
return parse_color_from_array (json_node_get_array (node), color);
|
|
|
|
|
|
|
|
case JSON_NODE_OBJECT:
|
|
|
|
return parse_color_from_object (json_node_get_object (node), color);
|
|
|
|
|
2009-11-04 15:21:03 +00:00
|
|
|
case JSON_NODE_VALUE:
|
|
|
|
return clutter_color_from_string (color, json_node_get_string (node));
|
|
|
|
|
2008-08-04 16:21:27 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2012-04-01 16:54:11 +00:00
|
|
|
static gboolean
|
|
|
|
parse_point_from_array (JsonArray *array,
|
|
|
|
ClutterPoint *point)
|
|
|
|
{
|
|
|
|
if (json_array_get_length (array) != 2)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
point->x = json_array_get_double_element (array, 0);
|
|
|
|
point->y = json_array_get_double_element (array, 1);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
parse_point_from_object (JsonObject *object,
|
|
|
|
ClutterPoint *point)
|
|
|
|
{
|
|
|
|
if (json_object_has_member (object, "x"))
|
|
|
|
point->x = json_object_get_double_member (object, "x");
|
|
|
|
else
|
|
|
|
point->x = 0.f;
|
|
|
|
|
|
|
|
if (json_object_has_member (object, "y"))
|
|
|
|
point->y = json_object_get_double_member (object, "y");
|
|
|
|
else
|
|
|
|
point->y = 0.f;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
_clutter_script_parse_point (ClutterScript *script,
|
|
|
|
JsonNode *node,
|
|
|
|
ClutterPoint *point)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
|
|
|
|
g_return_val_if_fail (node != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (point != NULL, FALSE);
|
|
|
|
|
|
|
|
switch (JSON_NODE_TYPE (node))
|
|
|
|
{
|
|
|
|
case JSON_NODE_ARRAY:
|
|
|
|
return parse_point_from_array (json_node_get_array (node), point);
|
|
|
|
|
|
|
|
case JSON_NODE_OBJECT:
|
|
|
|
return parse_point_from_object (json_node_get_object (node), point);
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
parse_size_from_array (JsonArray *array,
|
|
|
|
ClutterSize *size)
|
|
|
|
{
|
|
|
|
if (json_array_get_length (array) != 2)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
size->width = json_array_get_double_element (array, 0);
|
|
|
|
size->height = json_array_get_double_element (array, 1);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
parse_size_from_object (JsonObject *object,
|
|
|
|
ClutterSize *size)
|
|
|
|
{
|
|
|
|
if (json_object_has_member (object, "width"))
|
|
|
|
size->width = json_object_get_double_member (object, "width");
|
|
|
|
else
|
|
|
|
size->width = 0.f;
|
|
|
|
|
|
|
|
if (json_object_has_member (object, "height"))
|
|
|
|
size->height = json_object_get_double_member (object, "height");
|
|
|
|
else
|
|
|
|
size->height = 0.f;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
_clutter_script_parse_size (ClutterScript *script,
|
|
|
|
JsonNode *node,
|
|
|
|
ClutterSize *size)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
|
|
|
|
g_return_val_if_fail (node != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (size != NULL, FALSE);
|
|
|
|
|
|
|
|
switch (JSON_NODE_TYPE (node))
|
|
|
|
{
|
|
|
|
case JSON_NODE_ARRAY:
|
|
|
|
return parse_size_from_array (json_node_get_array (node), size);
|
|
|
|
|
|
|
|
case JSON_NODE_OBJECT:
|
|
|
|
return parse_size_from_object (json_node_get_object (node), size);
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2010-06-17 15:41:44 +00:00
|
|
|
const gchar *
|
|
|
|
_clutter_script_get_id_from_node (JsonNode *node)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
JsonObject *object;
|
|
|
|
|
|
|
|
switch (JSON_NODE_TYPE (node))
|
|
|
|
{
|
|
|
|
case JSON_NODE_OBJECT:
|
|
|
|
object = json_node_get_object (node);
|
|
|
|
if (json_object_has_member (object, "id"))
|
|
|
|
return json_object_get_string_member (object, "id");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSON_NODE_VALUE:
|
|
|
|
return json_node_get_string (node);
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GList *
|
|
|
|
parse_children (ObjectInfo *oinfo,
|
|
|
|
JsonNode *node)
|
|
|
|
{
|
|
|
|
JsonArray *array;
|
|
|
|
GList *retval;
|
|
|
|
guint array_len, i;
|
|
|
|
|
|
|
|
if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
retval = oinfo->children;
|
|
|
|
|
|
|
|
array = json_node_get_array (node);
|
|
|
|
array_len = json_array_get_length (array);
|
|
|
|
|
|
|
|
for (i = 0; i < array_len; i++)
|
|
|
|
{
|
|
|
|
JsonNode *child = json_array_get_element (array, i);
|
2011-02-15 11:50:26 +00:00
|
|
|
const gchar *id_;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2011-02-15 11:50:26 +00:00
|
|
|
id_ = _clutter_script_get_id_from_node (child);
|
|
|
|
if (id_ != NULL)
|
|
|
|
retval = g_list_prepend (retval, g_strdup (id_));
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return g_list_reverse (retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
static GList *
|
|
|
|
parse_signals (ClutterScript *script,
|
|
|
|
ObjectInfo *oinfo,
|
|
|
|
JsonNode *node)
|
|
|
|
{
|
|
|
|
JsonArray *array;
|
|
|
|
GList *retval;
|
|
|
|
guint array_len, i;
|
|
|
|
|
|
|
|
if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
|
|
|
|
{
|
|
|
|
_clutter_script_warn_invalid_value (script, "signals", "Array", node);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
retval = oinfo->signals;
|
|
|
|
array = json_node_get_array (node);
|
|
|
|
array_len = json_array_get_length (array);
|
|
|
|
|
|
|
|
for (i = 0; i < array_len; i++)
|
|
|
|
{
|
|
|
|
JsonNode *val = json_array_get_element (array, i);
|
script: Allow connecting signal to ClutterState states
One of the uses of a ClutterState state machine along with ClutterScript
is to provide a quick way to transition from state to state in response
to signal emitted on specific instances.
Connecting a real function, in code, to a specific signal does not
improve the ease of use of ClutterScript to define scenes.
By adding a new signal definition to the current one we can have both a
simple way to define application logic in code and in the UI definition
file.
The new syntax is trivial:
{
"name" : <signal name>,
"state" : <state machine script id>,
"target-state" : <target state>
}
The ClutterState instance is identified by its script id, and the target
state is resolved at run-time, so it can be defined both in
ClutterScript or in code. Ideally, we should find a way to associate a
default ClutterState instance to the ClutterScript one that parses the
definition; this way we would be able to remove the "state" member, or
even "style" the behaviour of an object by replacing the ClutterState
instance.
The implementation uses a signal emission hook, to avoid knowing the
signal signature; we check the emitter of the signal against the object
that defined the signal, to avoid erroneous state changes.
2011-02-07 13:48:58 +00:00
|
|
|
SignalInfo *sinfo = NULL;
|
2009-11-04 13:32:26 +00:00
|
|
|
JsonObject *object;
|
|
|
|
const gchar *name;
|
|
|
|
|
|
|
|
if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT)
|
|
|
|
{
|
|
|
|
_clutter_script_warn_invalid_value (script,
|
|
|
|
"signals array", "Object",
|
|
|
|
node);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
object = json_node_get_object (val);
|
|
|
|
|
|
|
|
/* mandatory: "name" */
|
|
|
|
if (!json_object_has_member (object, "name"))
|
|
|
|
{
|
|
|
|
_clutter_script_warn_missing_attribute (script, NULL, "name");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
name = json_object_get_string_member (object, "name");
|
|
|
|
if (!name)
|
|
|
|
{
|
|
|
|
_clutter_script_warn_invalid_value (script,
|
|
|
|
"name", "string",
|
|
|
|
val);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-23 16:37:40 +00:00
|
|
|
/* mandatory: "handler" */
|
|
|
|
if (json_object_has_member (object, "handler"))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
script: Allow connecting signal to ClutterState states
One of the uses of a ClutterState state machine along with ClutterScript
is to provide a quick way to transition from state to state in response
to signal emitted on specific instances.
Connecting a real function, in code, to a specific signal does not
improve the ease of use of ClutterScript to define scenes.
By adding a new signal definition to the current one we can have both a
simple way to define application logic in code and in the UI definition
file.
The new syntax is trivial:
{
"name" : <signal name>,
"state" : <state machine script id>,
"target-state" : <target state>
}
The ClutterState instance is identified by its script id, and the target
state is resolved at run-time, so it can be defined both in
ClutterScript or in code. Ideally, we should find a way to associate a
default ClutterState instance to the ClutterScript one that parses the
definition; this way we would be able to remove the "state" member, or
even "style" the behaviour of an object by replacing the ClutterState
instance.
The implementation uses a signal emission hook, to avoid knowing the
signal signature; we check the emitter of the signal against the object
that defined the signal, to avoid erroneous state changes.
2011-02-07 13:48:58 +00:00
|
|
|
const gchar *handler;
|
|
|
|
const gchar *connect;
|
|
|
|
GConnectFlags flags = 0;
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
handler = json_object_get_string_member (object, "handler");
|
script: Allow connecting signal to ClutterState states
One of the uses of a ClutterState state machine along with ClutterScript
is to provide a quick way to transition from state to state in response
to signal emitted on specific instances.
Connecting a real function, in code, to a specific signal does not
improve the ease of use of ClutterScript to define scenes.
By adding a new signal definition to the current one we can have both a
simple way to define application logic in code and in the UI definition
file.
The new syntax is trivial:
{
"name" : <signal name>,
"state" : <state machine script id>,
"target-state" : <target state>
}
The ClutterState instance is identified by its script id, and the target
state is resolved at run-time, so it can be defined both in
ClutterScript or in code. Ideally, we should find a way to associate a
default ClutterState instance to the ClutterScript one that parses the
definition; this way we would be able to remove the "state" member, or
even "style" the behaviour of an object by replacing the ClutterState
instance.
The implementation uses a signal emission hook, to avoid knowing the
signal signature; we check the emitter of the signal against the object
that defined the signal, to avoid erroneous state changes.
2011-02-07 13:48:58 +00:00
|
|
|
if (handler == NULL)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
_clutter_script_warn_invalid_value (script,
|
|
|
|
"handler", "string",
|
|
|
|
val);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
script: Allow connecting signal to ClutterState states
One of the uses of a ClutterState state machine along with ClutterScript
is to provide a quick way to transition from state to state in response
to signal emitted on specific instances.
Connecting a real function, in code, to a specific signal does not
improve the ease of use of ClutterScript to define scenes.
By adding a new signal definition to the current one we can have both a
simple way to define application logic in code and in the UI definition
file.
The new syntax is trivial:
{
"name" : <signal name>,
"state" : <state machine script id>,
"target-state" : <target state>
}
The ClutterState instance is identified by its script id, and the target
state is resolved at run-time, so it can be defined both in
ClutterScript or in code. Ideally, we should find a way to associate a
default ClutterState instance to the ClutterScript one that parses the
definition; this way we would be able to remove the "state" member, or
even "style" the behaviour of an object by replacing the ClutterState
instance.
The implementation uses a signal emission hook, to avoid knowing the
signal signature; we check the emitter of the signal against the object
that defined the signal, to avoid erroneous state changes.
2011-02-07 13:48:58 +00:00
|
|
|
/* optional: "object" */
|
|
|
|
if (json_object_has_member (object, "object"))
|
|
|
|
connect = json_object_get_string_member (object, "object");
|
|
|
|
else
|
|
|
|
connect = NULL;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
script: Allow connecting signal to ClutterState states
One of the uses of a ClutterState state machine along with ClutterScript
is to provide a quick way to transition from state to state in response
to signal emitted on specific instances.
Connecting a real function, in code, to a specific signal does not
improve the ease of use of ClutterScript to define scenes.
By adding a new signal definition to the current one we can have both a
simple way to define application logic in code and in the UI definition
file.
The new syntax is trivial:
{
"name" : <signal name>,
"state" : <state machine script id>,
"target-state" : <target state>
}
The ClutterState instance is identified by its script id, and the target
state is resolved at run-time, so it can be defined both in
ClutterScript or in code. Ideally, we should find a way to associate a
default ClutterState instance to the ClutterScript one that parses the
definition; this way we would be able to remove the "state" member, or
even "style" the behaviour of an object by replacing the ClutterState
instance.
The implementation uses a signal emission hook, to avoid knowing the
signal signature; we check the emitter of the signal against the object
that defined the signal, to avoid erroneous state changes.
2011-02-07 13:48:58 +00:00
|
|
|
/* optional: "after" */
|
|
|
|
if (json_object_has_member (object, "after"))
|
|
|
|
{
|
|
|
|
if (json_object_get_boolean_member (object, "after"))
|
|
|
|
flags |= G_CONNECT_AFTER;
|
|
|
|
}
|
2009-11-04 13:32:26 +00:00
|
|
|
|
script: Allow connecting signal to ClutterState states
One of the uses of a ClutterState state machine along with ClutterScript
is to provide a quick way to transition from state to state in response
to signal emitted on specific instances.
Connecting a real function, in code, to a specific signal does not
improve the ease of use of ClutterScript to define scenes.
By adding a new signal definition to the current one we can have both a
simple way to define application logic in code and in the UI definition
file.
The new syntax is trivial:
{
"name" : <signal name>,
"state" : <state machine script id>,
"target-state" : <target state>
}
The ClutterState instance is identified by its script id, and the target
state is resolved at run-time, so it can be defined both in
ClutterScript or in code. Ideally, we should find a way to associate a
default ClutterState instance to the ClutterScript one that parses the
definition; this way we would be able to remove the "state" member, or
even "style" the behaviour of an object by replacing the ClutterState
instance.
The implementation uses a signal emission hook, to avoid knowing the
signal signature; we check the emitter of the signal against the object
that defined the signal, to avoid erroneous state changes.
2011-02-07 13:48:58 +00:00
|
|
|
/* optional: "swapped" */
|
|
|
|
if (json_object_has_member (object, "swapped"))
|
|
|
|
{
|
|
|
|
if (json_object_get_boolean_member (object, "swapped"))
|
|
|
|
flags |= G_CONNECT_SWAPPED;
|
|
|
|
}
|
2009-11-04 13:32:26 +00:00
|
|
|
|
script: Allow connecting signal to ClutterState states
One of the uses of a ClutterState state machine along with ClutterScript
is to provide a quick way to transition from state to state in response
to signal emitted on specific instances.
Connecting a real function, in code, to a specific signal does not
improve the ease of use of ClutterScript to define scenes.
By adding a new signal definition to the current one we can have both a
simple way to define application logic in code and in the UI definition
file.
The new syntax is trivial:
{
"name" : <signal name>,
"state" : <state machine script id>,
"target-state" : <target state>
}
The ClutterState instance is identified by its script id, and the target
state is resolved at run-time, so it can be defined both in
ClutterScript or in code. Ideally, we should find a way to associate a
default ClutterState instance to the ClutterScript one that parses the
definition; this way we would be able to remove the "state" member, or
even "style" the behaviour of an object by replacing the ClutterState
instance.
The implementation uses a signal emission hook, to avoid knowing the
signal signature; we check the emitter of the signal against the object
that defined the signal, to avoid erroneous state changes.
2011-02-07 13:48:58 +00:00
|
|
|
CLUTTER_NOTE (SCRIPT,
|
|
|
|
"Added signal '%s' (handler:%s, object:%s, flags:%d)",
|
|
|
|
name,
|
|
|
|
handler, connect, flags);
|
|
|
|
|
|
|
|
sinfo = g_slice_new0 (SignalInfo);
|
|
|
|
sinfo->is_handler = TRUE;
|
|
|
|
sinfo->name = g_strdup (name);
|
|
|
|
sinfo->handler = g_strdup (handler);
|
|
|
|
sinfo->object = g_strdup (connect);
|
|
|
|
sinfo->flags = flags;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
_clutter_script_warn_missing_attribute (script,
|
|
|
|
NULL,
|
2012-08-23 16:37:40 +00:00
|
|
|
"handler");
|
2011-06-10 16:16:45 +00:00
|
|
|
if (sinfo != NULL)
|
|
|
|
retval = g_list_prepend (retval, sinfo);
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
gulong
|
2011-09-07 15:14:10 +00:00
|
|
|
_clutter_script_resolve_animation_mode (JsonNode *node)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2012-08-23 16:37:40 +00:00
|
|
|
gint res = CLUTTER_CUSTOM_MODE;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2010-02-08 15:45:43 +00:00
|
|
|
if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
|
|
|
|
return CLUTTER_CUSTOM_MODE;
|
|
|
|
|
|
|
|
if (json_node_get_value_type (node) == G_TYPE_INT64)
|
|
|
|
return json_node_get_int (node);
|
|
|
|
|
|
|
|
if (json_node_get_value_type (node) == G_TYPE_STRING)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2010-02-08 15:45:43 +00:00
|
|
|
const gchar *name = json_node_get_string (node);
|
|
|
|
|
2011-09-07 15:14:10 +00:00
|
|
|
if (_clutter_script_enum_from_string (CLUTTER_TYPE_ANIMATION_MODE,
|
|
|
|
name,
|
|
|
|
&res))
|
2010-02-08 15:45:43 +00:00
|
|
|
return res;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2010-02-08 15:45:43 +00:00
|
|
|
g_warning ("Unable to find the animation mode '%s'", name);
|
|
|
|
}
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
return CLUTTER_CUSTOM_MODE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_script_parser_object_end (JsonParser *json_parser,
|
|
|
|
JsonObject *object)
|
|
|
|
{
|
|
|
|
ClutterScriptParser *parser = CLUTTER_SCRIPT_PARSER (json_parser);
|
|
|
|
ClutterScript *script = parser->script;
|
|
|
|
ObjectInfo *oinfo;
|
|
|
|
JsonNode *val;
|
2011-02-15 11:50:26 +00:00
|
|
|
const gchar *id_;
|
2009-11-04 13:32:26 +00:00
|
|
|
GList *members, *l;
|
|
|
|
|
2010-03-18 20:20:09 +00:00
|
|
|
/* if the object definition does not have an 'id' field we'll
|
|
|
|
* fake one for it...
|
|
|
|
*/
|
2009-11-04 13:32:26 +00:00
|
|
|
if (!json_object_has_member (object, "id"))
|
|
|
|
{
|
|
|
|
gchar *fake;
|
|
|
|
|
2010-03-18 20:20:09 +00:00
|
|
|
/* ... unless it doesn't even have a type - in which case
|
|
|
|
* it is an internal object definition and we're not
|
|
|
|
* supposed to touch it
|
|
|
|
*/
|
2009-11-04 13:32:26 +00:00
|
|
|
if (!json_object_has_member (object, "type"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
fake = _clutter_script_generate_fake_id (script);
|
2010-03-18 17:44:14 +00:00
|
|
|
json_object_set_string_member (object, "id", fake);
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2010-03-18 20:20:09 +00:00
|
|
|
CLUTTER_NOTE (SCRIPT,
|
|
|
|
"Adding fake id '%s' to object of type '%s'",
|
|
|
|
json_object_get_string_member (object, "id"),
|
|
|
|
json_object_get_string_member (object, "type"));
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
g_free (fake);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!json_object_has_member (object, "type"))
|
|
|
|
{
|
|
|
|
val = json_object_get_member (object, "id");
|
|
|
|
|
|
|
|
_clutter_script_warn_missing_attribute (script,
|
|
|
|
json_node_get_string (val),
|
|
|
|
"type");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-15 11:50:26 +00:00
|
|
|
id_ = json_object_get_string_member (object, "id");
|
|
|
|
CLUTTER_NOTE (SCRIPT, "Getting object info for object '%s'", id_);
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2011-02-15 11:50:26 +00:00
|
|
|
oinfo = _clutter_script_get_object_info (script, id_);
|
2010-03-18 20:20:09 +00:00
|
|
|
if (oinfo == NULL)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
const gchar *class_name;
|
|
|
|
|
|
|
|
oinfo = g_slice_new0 (ObjectInfo);
|
|
|
|
oinfo->merge_id = _clutter_script_get_last_merge_id (script);
|
2011-02-15 11:50:26 +00:00
|
|
|
oinfo->id = g_strdup (id_);
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
class_name = json_object_get_string_member (object, "type");
|
|
|
|
oinfo->class_name = g_strdup (class_name);
|
|
|
|
|
|
|
|
if (json_object_has_member (object, "type_func"))
|
|
|
|
{
|
|
|
|
const gchar *type_func;
|
|
|
|
|
|
|
|
type_func = json_object_get_string_member (object, "type_func");
|
|
|
|
oinfo->type_func = g_strdup (type_func);
|
|
|
|
|
2010-03-18 20:20:09 +00:00
|
|
|
/* remove the type_func member; we don't want it to
|
|
|
|
* pollute the object members
|
|
|
|
*/
|
2009-11-04 13:32:26 +00:00
|
|
|
json_object_remove_member (object, "type_func");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (json_object_has_member (object, "children"))
|
|
|
|
{
|
|
|
|
val = json_object_get_member (object, "children");
|
|
|
|
oinfo->children = parse_children (oinfo, val);
|
|
|
|
|
|
|
|
json_object_remove_member (object, "children");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (json_object_has_member (object, "signals"))
|
|
|
|
{
|
|
|
|
val = json_object_get_member (object, "signals");
|
|
|
|
oinfo->signals = parse_signals (script, oinfo, val);
|
|
|
|
|
|
|
|
json_object_remove_member (object, "signals");
|
|
|
|
}
|
|
|
|
|
script: Fix the memory management
Currently, the memory management in ClutterScript is overly complicated.
The basic design tenet should be:
- ClutterScript owns a reference on every object it creates
This allows the Script instance to reliably handle the lifetime of the
instances from creation to disposal.
In case of unmerge, the Script instance should destroy any Actor
instance, except for the Stage, and release the reference it owns. The
Stage is special because it's really owned by Clutter itself, and it
should be destroyed explicitly.
When disposing the Script itself, it should just release the reference;
any parented actor, or any InitiallyUnowned instance, will then be
managed by the parent object, as they should, while every GObject
instance will go away, as documented.
This commit is based on a patch by:
Henrik Hedberg <hhedberg@innologies.fi>
http://bugzilla.clutter-project.org/show_bug.cgi?id=2316
2010-09-13 20:29:52 +00:00
|
|
|
oinfo->is_actor = FALSE;
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
if (strcmp (oinfo->class_name, "ClutterStage") == 0 &&
|
|
|
|
json_object_has_member (object, "is-default"))
|
|
|
|
{
|
script: Fix the memory management
Currently, the memory management in ClutterScript is overly complicated.
The basic design tenet should be:
- ClutterScript owns a reference on every object it creates
This allows the Script instance to reliably handle the lifetime of the
instances from creation to disposal.
In case of unmerge, the Script instance should destroy any Actor
instance, except for the Stage, and release the reference it owns. The
Stage is special because it's really owned by Clutter itself, and it
should be destroyed explicitly.
When disposing the Script itself, it should just release the reference;
any parented actor, or any InitiallyUnowned instance, will then be
managed by the parent object, as they should, while every GObject
instance will go away, as documented.
This commit is based on a patch by:
Henrik Hedberg <hhedberg@innologies.fi>
http://bugzilla.clutter-project.org/show_bug.cgi?id=2316
2010-09-13 20:29:52 +00:00
|
|
|
oinfo->is_actor = TRUE;
|
|
|
|
oinfo->is_stage = TRUE;
|
2009-11-04 13:32:26 +00:00
|
|
|
oinfo->is_stage_default =
|
|
|
|
json_object_get_boolean_member (object, "is-default");
|
|
|
|
|
|
|
|
json_object_remove_member (object, "is-default");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
oinfo->is_stage_default = FALSE;
|
|
|
|
|
|
|
|
oinfo->is_unmerged = FALSE;
|
|
|
|
oinfo->has_unresolved = TRUE;
|
|
|
|
|
|
|
|
members = json_object_get_members (object);
|
|
|
|
for (l = members; l; l = l->next)
|
|
|
|
{
|
|
|
|
const gchar *name = l->data;
|
|
|
|
PropertyInfo *pinfo;
|
|
|
|
JsonNode *node;
|
|
|
|
|
2010-03-18 20:20:09 +00:00
|
|
|
CLUTTER_NOTE (SCRIPT, "Object '%s' member '%s'",
|
|
|
|
oinfo->id,
|
|
|
|
name);
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
/* we have already parsed these */
|
|
|
|
if (strcmp (name, "id") == 0 || strcmp (name, "type") == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
node = json_object_get_member (object, name);
|
2010-03-18 20:20:09 +00:00
|
|
|
|
|
|
|
/* this should not really happen; we're getting a list of
|
|
|
|
* member names, and if one does not map a real member
|
|
|
|
* value then it's likely that something has gone wrong
|
|
|
|
*/
|
|
|
|
if (G_UNLIKELY (node == NULL))
|
2010-03-18 17:44:14 +00:00
|
|
|
{
|
|
|
|
CLUTTER_NOTE (SCRIPT,
|
|
|
|
"Empty node for member '%s' of object '%s' (type: %s)",
|
|
|
|
name,
|
|
|
|
oinfo->id,
|
|
|
|
oinfo->class_name);
|
|
|
|
continue;
|
|
|
|
}
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
pinfo = g_slice_new (PropertyInfo);
|
|
|
|
|
|
|
|
pinfo->name = g_strdup (name);
|
|
|
|
pinfo->node = json_node_copy (node);
|
|
|
|
pinfo->pspec = NULL;
|
2009-11-04 16:45:44 +00:00
|
|
|
pinfo->is_child = g_str_has_prefix (name, "child::") ? TRUE : FALSE;
|
2010-06-07 21:45:34 +00:00
|
|
|
pinfo->is_layout = g_str_has_prefix (name, "layout::") ? TRUE : FALSE;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (members);
|
|
|
|
|
|
|
|
CLUTTER_NOTE (SCRIPT,
|
|
|
|
"Added object '%s' (type:%s, id:%d, props:%d, signals:%d)",
|
|
|
|
oinfo->id,
|
|
|
|
oinfo->class_name,
|
|
|
|
oinfo->merge_id,
|
|
|
|
g_list_length (oinfo->properties),
|
|
|
|
g_list_length (oinfo->signals));
|
|
|
|
|
|
|
|
_clutter_script_add_object_info (script, oinfo);
|
2009-11-04 14:05:13 +00:00
|
|
|
_clutter_script_construct_object (script, oinfo);
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_script_parser_parse_end (JsonParser *parser)
|
|
|
|
{
|
|
|
|
clutter_script_ensure_objects (CLUTTER_SCRIPT_PARSER (parser)->script);
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:23:33 +00:00
|
|
|
gboolean
|
|
|
|
_clutter_script_parse_translatable_string (ClutterScript *script,
|
|
|
|
JsonNode *node,
|
|
|
|
char **str)
|
|
|
|
{
|
|
|
|
JsonObject *obj;
|
2012-03-17 22:55:33 +00:00
|
|
|
const char *string, *domain, *context;
|
2012-03-06 14:23:33 +00:00
|
|
|
const char *res;
|
|
|
|
gboolean translatable;
|
|
|
|
|
|
|
|
if (!JSON_NODE_HOLDS_OBJECT (node))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
obj = json_node_get_object (node);
|
|
|
|
if (!(json_object_has_member (obj, "translatable") &&
|
|
|
|
json_object_has_member (obj, "string")))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
translatable = json_object_get_boolean_member (obj, "translatable");
|
|
|
|
|
|
|
|
string = json_object_get_string_member (obj, "string");
|
|
|
|
if (string == NULL || *string == '\0')
|
|
|
|
return FALSE;
|
|
|
|
|
2012-03-17 22:55:33 +00:00
|
|
|
if (json_object_has_member (obj, "context"))
|
|
|
|
context = json_object_get_string_member (obj, "context");
|
|
|
|
else
|
|
|
|
context = NULL;
|
|
|
|
|
2012-03-06 14:23:33 +00:00
|
|
|
if (json_object_has_member (obj, "domain"))
|
|
|
|
domain = json_object_get_string_member (obj, "domain");
|
|
|
|
else
|
|
|
|
domain = NULL;
|
|
|
|
|
|
|
|
if (domain == NULL || *domain == '\0')
|
|
|
|
domain = clutter_script_get_translation_domain (script);
|
|
|
|
|
|
|
|
if (translatable)
|
|
|
|
{
|
2012-03-17 22:55:33 +00:00
|
|
|
if (context != NULL && *context != '\0')
|
2012-03-18 07:07:31 +00:00
|
|
|
res = g_dpgettext2 (domain, context, string);
|
2012-03-06 14:23:33 +00:00
|
|
|
else
|
2012-03-17 22:55:33 +00:00
|
|
|
res = g_dgettext (domain, string);
|
2012-03-06 14:23:33 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
res = string;
|
|
|
|
|
|
|
|
if (str != NULL)
|
|
|
|
*str = g_strdup (res);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
gboolean
|
2011-09-07 15:14:10 +00:00
|
|
|
_clutter_script_parse_node (ClutterScript *script,
|
|
|
|
GValue *value,
|
|
|
|
const gchar *name,
|
|
|
|
JsonNode *node,
|
|
|
|
GParamSpec *pspec)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2012-03-17 16:38:06 +00:00
|
|
|
GValue node_value = G_VALUE_INIT;
|
2009-11-04 13:32:26 +00:00
|
|
|
gboolean retval = FALSE;
|
|
|
|
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
|
|
|
|
g_return_val_if_fail (name != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (node != NULL, FALSE);
|
|
|
|
|
|
|
|
switch (JSON_NODE_TYPE (node))
|
|
|
|
{
|
|
|
|
case JSON_NODE_OBJECT:
|
2009-11-06 11:17:42 +00:00
|
|
|
/* if we don't have a GParamSpec we can't infer the type
|
|
|
|
* of the property; this usually means that this property
|
|
|
|
* is a custom member that will be parsed by the Scriptable
|
|
|
|
* interface implementantion
|
|
|
|
*/
|
2011-01-12 12:09:52 +00:00
|
|
|
if (pspec == NULL && !G_IS_VALUE (value))
|
2009-11-04 13:32:26 +00:00
|
|
|
return FALSE;
|
|
|
|
else
|
|
|
|
{
|
2011-01-12 12:09:52 +00:00
|
|
|
GType p_type;
|
2009-11-06 11:17:42 +00:00
|
|
|
ObjectInfo *oinfo;
|
2011-02-15 11:50:26 +00:00
|
|
|
const gchar *id_;
|
2009-11-06 11:17:42 +00:00
|
|
|
|
2011-01-12 12:09:52 +00:00
|
|
|
if (G_IS_VALUE (value))
|
|
|
|
p_type = G_VALUE_TYPE (value);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
|
|
|
|
g_value_init (value, p_type);
|
|
|
|
}
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2009-11-06 11:17:42 +00:00
|
|
|
if (g_type_is_a (p_type, G_TYPE_OBJECT))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2009-11-06 11:17:42 +00:00
|
|
|
/* default GObject handling: we get the id and
|
|
|
|
* retrieve the ObjectInfo for it; since the object
|
|
|
|
* definitions are parsed leaf-first we are guaranteed
|
|
|
|
* to have a defined object at this point
|
|
|
|
*/
|
2011-02-15 11:50:26 +00:00
|
|
|
id_ = _clutter_script_get_id_from_node (node);
|
|
|
|
if (id_ == NULL || *id_ == '\0')
|
2009-11-06 11:17:42 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2011-02-15 11:50:26 +00:00
|
|
|
oinfo = _clutter_script_get_object_info (script, id_);
|
2009-11-06 11:17:42 +00:00
|
|
|
if (oinfo == NULL || oinfo->gtype == G_TYPE_INVALID )
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (g_type_is_a (oinfo->gtype, p_type))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2009-11-06 11:17:42 +00:00
|
|
|
/* force construction, even though it should
|
|
|
|
* not be necessary; we don't need the properties
|
|
|
|
* to be applied as well: they will when the
|
|
|
|
* ScriptParser finishes
|
|
|
|
*/
|
|
|
|
_clutter_script_construct_object (script, oinfo);
|
|
|
|
|
|
|
|
g_value_set_object (value, oinfo->object);
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2009-11-06 11:17:42 +00:00
|
|
|
else if (p_type == CLUTTER_TYPE_KNOT)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
ClutterKnot knot = { 0, };
|
|
|
|
|
|
|
|
/* knot := { "x" : (int), "y" : (int) } */
|
|
|
|
|
2011-09-07 15:14:10 +00:00
|
|
|
if (_clutter_script_parse_knot (script, node, &knot))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
g_value_set_boxed (value, &knot);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2013-03-30 00:24:17 +00:00
|
|
|
else if (p_type == CLUTTER_TYPE_RECT)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2013-03-30 00:24:17 +00:00
|
|
|
ClutterRect rect = CLUTTER_RECT_INIT_ZERO;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2013-03-30 00:24:17 +00:00
|
|
|
/* rect := {
|
|
|
|
* "x" : (double),
|
|
|
|
* "y" : (double),
|
|
|
|
* "width" : (double),
|
|
|
|
* "height" : (double)
|
2009-11-04 13:32:26 +00:00
|
|
|
* }
|
|
|
|
*/
|
|
|
|
|
2013-03-30 00:24:17 +00:00
|
|
|
if (_clutter_script_parse_rect (script, node, &rect))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2013-03-30 00:24:17 +00:00
|
|
|
g_value_set_boxed (value, &rect);
|
2009-11-04 13:32:26 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2009-11-06 11:17:42 +00:00
|
|
|
else if (p_type == CLUTTER_TYPE_COLOR)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
ClutterColor color = { 0, };
|
|
|
|
|
|
|
|
/* color := {
|
|
|
|
* "red" : (int),
|
|
|
|
* "green" : (int),
|
|
|
|
* "blue" : (int),
|
|
|
|
* "alpha" : (int)
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
|
2011-09-07 15:14:10 +00:00
|
|
|
if (_clutter_script_parse_color (script, node, &color))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
g_value_set_boxed (value, &color);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2012-04-01 16:54:11 +00:00
|
|
|
else if (p_type == CLUTTER_TYPE_POINT)
|
|
|
|
{
|
|
|
|
ClutterPoint point = CLUTTER_POINT_INIT_ZERO;
|
|
|
|
|
|
|
|
if (_clutter_script_parse_point (script, node, &point))
|
|
|
|
{
|
|
|
|
g_value_set_boxed (value, &point);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (p_type == CLUTTER_TYPE_SIZE)
|
|
|
|
{
|
|
|
|
ClutterSize size = CLUTTER_SIZE_INIT_ZERO;
|
|
|
|
|
|
|
|
if (_clutter_script_parse_size (script, node, &size))
|
|
|
|
{
|
|
|
|
g_value_set_boxed (value, &size);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2012-03-06 14:23:33 +00:00
|
|
|
else if (p_type == G_TYPE_STRING)
|
|
|
|
{
|
|
|
|
char *str = NULL;
|
|
|
|
|
|
|
|
if (_clutter_script_parse_translatable_string (script, node, &str))
|
|
|
|
{
|
|
|
|
g_value_take_string (value, str);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
case JSON_NODE_ARRAY:
|
2011-01-12 12:09:52 +00:00
|
|
|
if (!pspec && !G_IS_VALUE (value))
|
2009-11-04 13:32:26 +00:00
|
|
|
return FALSE;
|
|
|
|
else
|
|
|
|
{
|
2011-01-12 12:09:52 +00:00
|
|
|
if (!G_IS_VALUE (value))
|
|
|
|
g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
if (G_VALUE_HOLDS (value, CLUTTER_TYPE_KNOT))
|
|
|
|
{
|
|
|
|
ClutterKnot knot = { 0, };
|
|
|
|
|
|
|
|
/* knot := [ (int), (int) ] */
|
|
|
|
|
2011-09-07 15:14:10 +00:00
|
|
|
if (_clutter_script_parse_knot (script, node, &knot))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
g_value_set_boxed (value, &knot);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2013-03-30 00:24:17 +00:00
|
|
|
else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_RECT))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2013-03-30 00:24:17 +00:00
|
|
|
ClutterRect rect = CLUTTER_RECT_INIT_ZERO;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2013-03-30 00:24:17 +00:00
|
|
|
/* rect := [ (double), (double), (double), (double) ] */
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2013-03-30 00:24:17 +00:00
|
|
|
if (_clutter_script_parse_rect (script, node, &rect))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2013-03-30 00:24:17 +00:00
|
|
|
g_value_set_boxed (value, &rect);
|
2009-11-04 13:32:26 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CLUTTER_VALUE_HOLDS_COLOR (value))
|
|
|
|
{
|
|
|
|
ClutterColor color = { 0, };
|
|
|
|
|
|
|
|
/* color := [ (int), (int), (int), (int) ] */
|
|
|
|
|
2011-09-07 15:14:10 +00:00
|
|
|
if (_clutter_script_parse_color (script, node, &color))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
g_value_set_boxed (value, &color);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2012-04-01 16:54:11 +00:00
|
|
|
else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_POINT))
|
|
|
|
{
|
|
|
|
ClutterPoint point = CLUTTER_POINT_INIT_ZERO;
|
|
|
|
|
|
|
|
if (_clutter_script_parse_point (script, node, &point))
|
|
|
|
{
|
|
|
|
g_value_set_boxed (value, &point);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_SIZE))
|
|
|
|
{
|
|
|
|
ClutterSize size = CLUTTER_SIZE_INIT_ZERO;
|
|
|
|
|
|
|
|
if (_clutter_script_parse_size (script, node, &size))
|
|
|
|
{
|
|
|
|
g_value_set_boxed (value, &size);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2009-11-04 13:32:26 +00:00
|
|
|
else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
|
|
|
|
{
|
|
|
|
JsonArray *array = json_node_get_array (node);
|
|
|
|
guint i, array_len = json_array_get_length (array);
|
|
|
|
GPtrArray *str_array = g_ptr_array_sized_new (array_len);
|
|
|
|
|
|
|
|
/* strv := [ (str), (str), ... ] */
|
|
|
|
|
|
|
|
for (i = 0; i < array_len; i++)
|
|
|
|
{
|
|
|
|
JsonNode *val = json_array_get_element (array, i);
|
|
|
|
|
|
|
|
if (JSON_NODE_TYPE (val) != JSON_NODE_VALUE &&
|
|
|
|
json_node_get_string (val) == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
g_ptr_array_add (str_array,
|
|
|
|
(gpointer) json_node_get_string (val));
|
|
|
|
}
|
|
|
|
|
|
|
|
g_value_set_boxed (value, str_array->pdata);
|
|
|
|
g_ptr_array_free (str_array, TRUE);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
case JSON_NODE_NULL:
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
case JSON_NODE_VALUE:
|
|
|
|
json_node_get_value (node, &node_value);
|
|
|
|
|
2011-01-12 12:09:52 +00:00
|
|
|
if (!pspec && !G_IS_VALUE (value))
|
2009-11-04 13:32:26 +00:00
|
|
|
g_value_init (value, G_VALUE_TYPE (&node_value));
|
2011-01-12 12:09:52 +00:00
|
|
|
else if (pspec)
|
|
|
|
g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)))
|
|
|
|
{
|
|
|
|
/* fundamental JSON types */
|
|
|
|
case G_TYPE_INT64:
|
|
|
|
case G_TYPE_DOUBLE:
|
|
|
|
case G_TYPE_STRING:
|
|
|
|
case G_TYPE_BOOLEAN:
|
|
|
|
g_value_copy (&node_value, value);
|
|
|
|
retval = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_TYPE_INT:
|
|
|
|
g_value_set_int (value, g_value_get_int64 (&node_value));
|
|
|
|
retval = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_TYPE_UINT:
|
|
|
|
g_value_set_uint (value, (guint) g_value_get_int64 (&node_value));
|
|
|
|
retval = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_TYPE_ULONG:
|
|
|
|
g_value_set_ulong (value, (gulong) g_value_get_int64 (&node_value));
|
|
|
|
retval = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_TYPE_UCHAR:
|
|
|
|
g_value_set_uchar (value, (guchar) g_value_get_int64 (&node_value));
|
|
|
|
retval = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_TYPE_FLOAT:
|
|
|
|
if (G_VALUE_HOLDS (&node_value, G_TYPE_DOUBLE))
|
|
|
|
{
|
|
|
|
g_value_set_float (value, g_value_get_double (&node_value));
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
else if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
|
|
|
|
{
|
|
|
|
g_value_set_float (value, g_value_get_int64 (&node_value));
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_TYPE_ENUM:
|
|
|
|
if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
|
|
|
|
{
|
|
|
|
g_value_set_enum (value, g_value_get_int64 (&node_value));
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
|
|
|
|
{
|
|
|
|
gint enum_value;
|
|
|
|
|
2011-09-07 15:14:10 +00:00
|
|
|
retval = _clutter_script_enum_from_string (G_VALUE_TYPE (value),
|
|
|
|
g_value_get_string (&node_value),
|
|
|
|
&enum_value);
|
2009-11-04 13:32:26 +00:00
|
|
|
if (retval)
|
|
|
|
g_value_set_enum (value, enum_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_TYPE_FLAGS:
|
|
|
|
if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
|
|
|
|
{
|
|
|
|
g_value_set_flags (value, g_value_get_int64 (&node_value));
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
|
|
|
|
{
|
|
|
|
gint flags_value;
|
|
|
|
|
2011-09-07 15:14:10 +00:00
|
|
|
retval = _clutter_script_flags_from_string (G_VALUE_TYPE (value),
|
|
|
|
g_value_get_string (&node_value),
|
|
|
|
&flags_value);
|
2009-11-04 13:32:26 +00:00
|
|
|
if (retval)
|
|
|
|
g_value_set_flags (value, flags_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_TYPE_BOXED:
|
|
|
|
if (G_VALUE_HOLDS (value, CLUTTER_TYPE_COLOR))
|
|
|
|
{
|
2009-11-04 15:21:03 +00:00
|
|
|
ClutterColor color = { 0, };
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2011-09-07 15:14:10 +00:00
|
|
|
retval = _clutter_script_parse_color (script, node, &color);
|
2009-11-04 15:21:03 +00:00
|
|
|
if (retval)
|
|
|
|
clutter_value_set_color (value, &color);
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_TYPE_OBJECT:
|
|
|
|
if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
|
|
|
|
{
|
|
|
|
const gchar *str = g_value_get_string (&node_value);
|
|
|
|
GObject *object = clutter_script_get_object (script, str);
|
|
|
|
if (object)
|
|
|
|
{
|
|
|
|
CLUTTER_NOTE (SCRIPT,
|
|
|
|
"Assigning '%s' (%s) to property '%s'",
|
|
|
|
str,
|
|
|
|
G_OBJECT_TYPE_NAME (object),
|
|
|
|
name);
|
|
|
|
|
|
|
|
g_value_set_object (value, object);
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
retval = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-01-03 15:15:17 +00:00
|
|
|
if (G_VALUE_TYPE (value) == G_TYPE_GTYPE &&
|
|
|
|
G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
|
|
|
|
{
|
|
|
|
const gchar *str = g_value_get_string (&node_value);
|
|
|
|
GType type = clutter_script_get_type_from_name (script, str);
|
|
|
|
g_value_set_gtype (value, type);
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
g_value_unset (&node_value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GList *
|
|
|
|
clutter_script_translate_parameters (ClutterScript *script,
|
|
|
|
GObject *object,
|
|
|
|
const gchar *name,
|
|
|
|
GList *properties,
|
|
|
|
GArray **params)
|
|
|
|
{
|
|
|
|
ClutterScriptable *scriptable = NULL;
|
|
|
|
ClutterScriptableIface *iface = NULL;
|
|
|
|
GList *l, *unparsed;
|
|
|
|
gboolean parse_custom = FALSE;
|
|
|
|
|
|
|
|
*params = g_array_new (FALSE, FALSE, sizeof (GParameter));
|
|
|
|
|
|
|
|
if (CLUTTER_IS_SCRIPTABLE (object))
|
|
|
|
{
|
|
|
|
scriptable = CLUTTER_SCRIPTABLE (object);
|
|
|
|
iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
|
|
|
|
|
|
|
|
if (iface->parse_custom_node)
|
|
|
|
parse_custom = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
unparsed = NULL;
|
|
|
|
|
|
|
|
for (l = properties; l != NULL; l = l->next)
|
|
|
|
{
|
|
|
|
PropertyInfo *pinfo = l->data;
|
|
|
|
GParameter param = { NULL };
|
|
|
|
gboolean res = FALSE;
|
|
|
|
|
2010-06-07 21:45:34 +00:00
|
|
|
if (pinfo->is_child || pinfo->is_layout)
|
2009-11-04 16:45:44 +00:00
|
|
|
{
|
2010-06-07 21:45:34 +00:00
|
|
|
CLUTTER_NOTE (SCRIPT, "Skipping %s property '%s'",
|
|
|
|
pinfo->is_child ? "child" : "layout",
|
|
|
|
pinfo->name);
|
2009-11-04 16:45:44 +00:00
|
|
|
unparsed = g_list_prepend (unparsed, pinfo);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
CLUTTER_NOTE (SCRIPT, "Parsing %s property (id:%s)",
|
|
|
|
pinfo->pspec ? "regular" : "custom",
|
|
|
|
pinfo->name);
|
|
|
|
|
|
|
|
if (parse_custom)
|
|
|
|
res = iface->parse_custom_node (scriptable, script, ¶m.value,
|
|
|
|
pinfo->name,
|
|
|
|
pinfo->node);
|
|
|
|
|
|
|
|
if (!res)
|
2011-09-07 15:14:10 +00:00
|
|
|
res = _clutter_script_parse_node (script, ¶m.value,
|
|
|
|
pinfo->name,
|
|
|
|
pinfo->node,
|
|
|
|
pinfo->pspec);
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
if (!res)
|
|
|
|
{
|
|
|
|
CLUTTER_NOTE (SCRIPT, "Property '%s' ignored", pinfo->name);
|
|
|
|
unparsed = g_list_prepend (unparsed, pinfo);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
param.name = g_strdup (pinfo->name);
|
|
|
|
|
|
|
|
g_array_append_val (*params, param);
|
|
|
|
|
|
|
|
property_info_free (pinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (properties);
|
|
|
|
|
|
|
|
return unparsed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GList *
|
|
|
|
clutter_script_construct_parameters (ClutterScript *script,
|
|
|
|
GType gtype,
|
|
|
|
const gchar *name,
|
|
|
|
GList *properties,
|
|
|
|
GArray **construct_params)
|
|
|
|
{
|
|
|
|
GObjectClass *klass;
|
|
|
|
GList *l, *unparsed;
|
|
|
|
|
|
|
|
klass = g_type_class_ref (gtype);
|
|
|
|
g_assert (klass != NULL);
|
|
|
|
|
|
|
|
*construct_params = g_array_new (FALSE, FALSE, sizeof (GParameter));
|
|
|
|
|
|
|
|
unparsed = NULL;
|
|
|
|
|
|
|
|
for (l = properties; l != NULL; l = l->next)
|
|
|
|
{
|
|
|
|
PropertyInfo *pinfo = l->data;
|
|
|
|
GParameter param = { NULL };
|
|
|
|
GParamSpec *pspec = NULL;
|
|
|
|
|
|
|
|
/* we allow custom property names for classes, so if we
|
|
|
|
* don't find a corresponding GObject property for this
|
|
|
|
* class we just skip it and let the class itself deal
|
|
|
|
* with it later on
|
|
|
|
*/
|
|
|
|
pspec = g_object_class_find_property (klass, pinfo->name);
|
|
|
|
if (pspec)
|
|
|
|
pinfo->pspec = g_param_spec_ref (pspec);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pinfo->pspec = NULL;
|
|
|
|
unparsed = g_list_prepend (unparsed, pinfo);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY))
|
|
|
|
{
|
|
|
|
unparsed = g_list_prepend (unparsed, pinfo);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
param.name = g_strdup (pinfo->name);
|
|
|
|
|
2011-09-07 15:14:10 +00:00
|
|
|
if (!_clutter_script_parse_node (script, ¶m.value,
|
|
|
|
pinfo->name,
|
|
|
|
pinfo->node,
|
|
|
|
pinfo->pspec))
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
unparsed = g_list_prepend (unparsed, pinfo);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_append_val (*construct_params, param);
|
|
|
|
|
|
|
|
property_info_free (pinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (properties);
|
|
|
|
|
|
|
|
g_type_class_unref (klass);
|
|
|
|
|
|
|
|
return unparsed;
|
|
|
|
}
|
|
|
|
|
2010-06-07 21:45:34 +00:00
|
|
|
static void
|
|
|
|
apply_layout_properties (ClutterScript *script,
|
|
|
|
ClutterContainer *container,
|
|
|
|
ClutterActor *actor,
|
|
|
|
ObjectInfo *oinfo)
|
|
|
|
{
|
|
|
|
ClutterScriptable *scriptable = NULL;
|
|
|
|
ClutterScriptableIface *iface = NULL;
|
|
|
|
gboolean parse_custom_node = FALSE;
|
|
|
|
GList *l, *unresolved, *properties;
|
|
|
|
ClutterLayoutManager *manager;
|
|
|
|
GType meta_type;
|
|
|
|
|
2012-08-23 16:37:40 +00:00
|
|
|
/* XXX:2.0 remove; only ClutterActor should have a layout manager */
|
|
|
|
manager = g_object_get_data (G_OBJECT (container), "-clutter-layout-manager");
|
2010-06-07 21:45:34 +00:00
|
|
|
if (manager == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
meta_type = _clutter_layout_manager_get_child_meta_type (manager);
|
|
|
|
if (meta_type == G_TYPE_INVALID)
|
|
|
|
return;
|
|
|
|
|
|
|
|
CLUTTER_NOTE (SCRIPT, "Layout manager of type '%s' with meta type '%s'",
|
|
|
|
G_OBJECT_TYPE_NAME (manager),
|
|
|
|
g_type_name (meta_type));
|
|
|
|
|
|
|
|
/* shortcut, to avoid typechecking every time */
|
|
|
|
if (CLUTTER_IS_SCRIPTABLE (manager))
|
|
|
|
{
|
|
|
|
scriptable = CLUTTER_SCRIPTABLE (manager);
|
|
|
|
iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
|
|
|
|
|
|
|
|
parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
properties = oinfo->properties;
|
|
|
|
oinfo->properties = NULL;
|
|
|
|
|
|
|
|
unresolved = NULL;
|
|
|
|
for (l = properties; l != NULL; l = l->next)
|
|
|
|
{
|
|
|
|
PropertyInfo *pinfo = l->data;
|
2012-03-17 16:38:06 +00:00
|
|
|
GValue value = G_VALUE_INIT;
|
2010-06-07 21:45:34 +00:00
|
|
|
gboolean res = FALSE;
|
|
|
|
const gchar *name;
|
|
|
|
|
|
|
|
if (!pinfo->is_layout)
|
|
|
|
{
|
|
|
|
unresolved = g_list_prepend (unresolved, pinfo);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
name = pinfo->name + strlen ("layout::");
|
|
|
|
|
|
|
|
pinfo->pspec =
|
|
|
|
clutter_layout_manager_find_child_property (manager, name);
|
|
|
|
|
|
|
|
if (pinfo->pspec != NULL)
|
|
|
|
g_param_spec_ref (pinfo->pspec);
|
|
|
|
|
|
|
|
CLUTTER_NOTE (SCRIPT, "Parsing %s layout property (id:%s)",
|
|
|
|
pinfo->pspec != NULL ? "regular" : "custom",
|
|
|
|
name);
|
|
|
|
|
|
|
|
if (parse_custom_node)
|
|
|
|
res = iface->parse_custom_node (scriptable, script, &value,
|
|
|
|
name,
|
|
|
|
pinfo->node);
|
|
|
|
|
|
|
|
if (!res)
|
2011-09-07 15:14:10 +00:00
|
|
|
res = _clutter_script_parse_node (script, &value,
|
|
|
|
name,
|
|
|
|
pinfo->node,
|
|
|
|
pinfo->pspec);
|
2010-06-07 21:45:34 +00:00
|
|
|
|
|
|
|
if (!res)
|
|
|
|
{
|
|
|
|
CLUTTER_NOTE (SCRIPT, "Layout property '%s' ignored", name);
|
|
|
|
unresolved = g_list_prepend (unresolved, pinfo);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
CLUTTER_NOTE (SCRIPT,
|
|
|
|
"Setting %s layout property '%s' (type:%s) to "
|
|
|
|
"object '%s' (id:%s)",
|
2013-03-25 17:57:28 +00:00
|
|
|
iface->set_custom_property != NULL ? "custom" : "regular",
|
2010-06-07 21:45:34 +00:00
|
|
|
name,
|
|
|
|
g_type_name (G_VALUE_TYPE (&value)),
|
|
|
|
g_type_name (oinfo->gtype),
|
|
|
|
oinfo->id);
|
|
|
|
|
|
|
|
clutter_layout_manager_child_set_property (manager, container, actor,
|
|
|
|
name,
|
|
|
|
&value);
|
|
|
|
|
|
|
|
g_value_unset (&value);
|
|
|
|
|
|
|
|
property_info_free (pinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (properties);
|
|
|
|
|
|
|
|
oinfo->properties = unresolved;
|
|
|
|
}
|
|
|
|
|
2009-11-04 16:45:44 +00:00
|
|
|
static void
|
|
|
|
apply_child_properties (ClutterScript *script,
|
|
|
|
ClutterContainer *container,
|
|
|
|
ClutterActor *actor,
|
|
|
|
ObjectInfo *oinfo)
|
|
|
|
{
|
|
|
|
ClutterScriptable *scriptable = NULL;
|
|
|
|
ClutterScriptableIface *iface = NULL;
|
|
|
|
gboolean parse_custom_node = FALSE;
|
|
|
|
GList *l, *unresolved, *properties;
|
|
|
|
GObjectClass *klass;
|
|
|
|
GType meta_type;
|
|
|
|
|
|
|
|
meta_type = CLUTTER_CONTAINER_GET_IFACE (container)->child_meta_type;
|
|
|
|
if (meta_type == G_TYPE_INVALID)
|
|
|
|
return;
|
|
|
|
|
|
|
|
klass = G_OBJECT_GET_CLASS (container);
|
|
|
|
|
|
|
|
/* shortcut, to avoid typechecking every time */
|
|
|
|
if (CLUTTER_IS_SCRIPTABLE (container))
|
|
|
|
{
|
|
|
|
scriptable = CLUTTER_SCRIPTABLE (container);
|
|
|
|
iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
|
|
|
|
|
|
|
|
parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
properties = oinfo->properties;
|
|
|
|
oinfo->properties = NULL;
|
|
|
|
|
|
|
|
unresolved = NULL;
|
|
|
|
for (l = properties; l != NULL; l = l->next)
|
|
|
|
{
|
|
|
|
PropertyInfo *pinfo = l->data;
|
2012-03-17 16:38:06 +00:00
|
|
|
GValue value = G_VALUE_INIT;
|
2010-02-11 15:17:53 +00:00
|
|
|
gboolean res = FALSE;
|
2009-11-04 16:45:44 +00:00
|
|
|
const gchar *name;
|
|
|
|
|
|
|
|
if (!pinfo->is_child)
|
|
|
|
{
|
|
|
|
unresolved = g_list_prepend (unresolved, pinfo);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
name = pinfo->name + strlen ("child::");
|
|
|
|
|
|
|
|
pinfo->pspec =
|
|
|
|
clutter_container_class_find_child_property (klass, name);
|
|
|
|
|
|
|
|
if (pinfo->pspec != NULL)
|
|
|
|
g_param_spec_ref (pinfo->pspec);
|
|
|
|
|
|
|
|
CLUTTER_NOTE (SCRIPT, "Parsing %s child property (id:%s)",
|
|
|
|
pinfo->pspec != NULL ? "regular" : "custom",
|
|
|
|
name);
|
|
|
|
|
|
|
|
if (parse_custom_node)
|
|
|
|
res = iface->parse_custom_node (scriptable, script, &value,
|
|
|
|
name,
|
|
|
|
pinfo->node);
|
|
|
|
|
|
|
|
if (!res)
|
2011-09-07 15:14:10 +00:00
|
|
|
res = _clutter_script_parse_node (script, &value,
|
|
|
|
name,
|
|
|
|
pinfo->node,
|
|
|
|
pinfo->pspec);
|
2009-11-04 16:45:44 +00:00
|
|
|
|
|
|
|
if (!res)
|
|
|
|
{
|
|
|
|
CLUTTER_NOTE (SCRIPT, "Child property '%s' ignored", name);
|
|
|
|
unresolved = g_list_prepend (unresolved, pinfo);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CLUTTER_NOTE (SCRIPT,
|
|
|
|
"Setting %s child property '%s' (type:%s) to "
|
|
|
|
"object '%s' (id:%s)",
|
2013-03-25 17:57:28 +00:00
|
|
|
iface->set_custom_property != NULL ? "custom" : "regular",
|
2009-11-04 16:45:44 +00:00
|
|
|
name,
|
|
|
|
g_type_name (G_VALUE_TYPE (&value)),
|
|
|
|
g_type_name (oinfo->gtype),
|
|
|
|
oinfo->id);
|
|
|
|
|
|
|
|
clutter_container_child_set_property (container, actor,
|
|
|
|
name,
|
|
|
|
&value);
|
|
|
|
|
|
|
|
g_value_unset (&value);
|
|
|
|
|
|
|
|
property_info_free (pinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (properties);
|
|
|
|
|
|
|
|
oinfo->properties = unresolved;
|
|
|
|
}
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
static void
|
2009-11-04 14:05:13 +00:00
|
|
|
add_children (ClutterScript *script,
|
|
|
|
ObjectInfo *oinfo)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2012-08-23 16:37:40 +00:00
|
|
|
ClutterActor *container = CLUTTER_ACTOR (oinfo->object);
|
2009-11-04 13:32:26 +00:00
|
|
|
GList *l, *unresolved;
|
|
|
|
|
|
|
|
unresolved = NULL;
|
|
|
|
for (l = oinfo->children; l != NULL; l = l->next)
|
|
|
|
{
|
|
|
|
const gchar *name = l->data;
|
2009-11-04 14:05:13 +00:00
|
|
|
GObject *object = NULL;
|
|
|
|
ObjectInfo *child_info;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2009-11-04 14:05:13 +00:00
|
|
|
child_info = _clutter_script_get_object_info (script, name);
|
|
|
|
if (child_info != NULL)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2009-11-04 14:05:13 +00:00
|
|
|
_clutter_script_construct_object (script, child_info);
|
|
|
|
object = child_info->object;
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
|
2009-11-04 14:05:13 +00:00
|
|
|
if (object == NULL)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
|
|
|
unresolved = g_list_prepend (unresolved, g_strdup (name));
|
|
|
|
continue;
|
2010-02-25 14:20:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!CLUTTER_IS_ACTOR (object))
|
|
|
|
{
|
|
|
|
g_warning ("The object definition '%s' (type: %s) is not "
|
|
|
|
"an actor, but it is referenced in the 'children' "
|
|
|
|
"member of the container '%s' (type: %s); skipping.",
|
|
|
|
child_info->id,
|
|
|
|
g_type_name (child_info->gtype),
|
|
|
|
oinfo->id,
|
|
|
|
g_type_name (oinfo->gtype));
|
|
|
|
continue;
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CLUTTER_NOTE (SCRIPT, "Adding children '%s' to actor of type '%s'",
|
|
|
|
name,
|
|
|
|
g_type_name (G_OBJECT_TYPE (container)));
|
|
|
|
|
2012-08-23 16:37:40 +00:00
|
|
|
clutter_actor_add_child (container, CLUTTER_ACTOR (object));
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_list_foreach (oinfo->children, (GFunc) g_free, NULL);
|
|
|
|
g_list_free (oinfo->children);
|
|
|
|
|
|
|
|
oinfo->children = unresolved;
|
|
|
|
}
|
|
|
|
|
2009-11-04 14:05:13 +00:00
|
|
|
static inline void
|
|
|
|
_clutter_script_check_unresolved (ClutterScript *script,
|
|
|
|
ObjectInfo *oinfo)
|
|
|
|
{
|
|
|
|
if (oinfo->children != NULL && CLUTTER_IS_CONTAINER (oinfo->object))
|
|
|
|
add_children (script, oinfo);
|
|
|
|
|
2010-06-07 21:45:34 +00:00
|
|
|
/* this is a bit *eugh*, but it allows us to effectively make sure
|
|
|
|
* that child and layout properties are parsed and applied to the
|
|
|
|
* right child
|
|
|
|
*/
|
|
|
|
if (oinfo->properties != NULL && CLUTTER_IS_ACTOR (oinfo->object))
|
|
|
|
{
|
|
|
|
ClutterActor *parent;
|
|
|
|
|
|
|
|
parent = clutter_actor_get_parent (CLUTTER_ACTOR (oinfo->object));
|
2011-12-18 22:35:45 +00:00
|
|
|
if (parent != NULL)
|
2010-06-07 21:45:34 +00:00
|
|
|
{
|
|
|
|
ClutterContainer *container = CLUTTER_CONTAINER (parent);
|
2011-12-18 22:35:45 +00:00
|
|
|
ClutterActor *child;
|
2010-06-07 21:45:34 +00:00
|
|
|
|
2011-12-18 22:35:45 +00:00
|
|
|
for (child = clutter_actor_get_first_child (parent);
|
|
|
|
child != NULL;
|
|
|
|
child = clutter_actor_get_next_sibling (child))
|
2010-06-07 21:45:34 +00:00
|
|
|
{
|
|
|
|
ObjectInfo *child_info;
|
2011-02-15 11:50:26 +00:00
|
|
|
const gchar *id_;
|
2010-06-07 21:45:34 +00:00
|
|
|
|
2011-12-18 22:35:45 +00:00
|
|
|
id_ = clutter_get_script_id (G_OBJECT (child));
|
2011-02-15 11:50:26 +00:00
|
|
|
if (id_ == NULL || *id_ == '\0')
|
2010-06-07 21:45:34 +00:00
|
|
|
continue;
|
|
|
|
|
2011-02-15 11:50:26 +00:00
|
|
|
child_info = _clutter_script_get_object_info (script, id_);
|
2010-06-07 21:45:34 +00:00
|
|
|
if (child_info == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
apply_child_properties (script, container,
|
2011-12-18 22:35:45 +00:00
|
|
|
child,
|
2010-06-07 21:45:34 +00:00
|
|
|
child_info);
|
|
|
|
apply_layout_properties (script, container,
|
2011-12-18 22:35:45 +00:00
|
|
|
child,
|
2010-06-07 21:45:34 +00:00
|
|
|
child_info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-09 13:59:32 +00:00
|
|
|
if (oinfo->properties || oinfo->children)
|
2009-11-04 14:05:13 +00:00
|
|
|
oinfo->has_unresolved = TRUE;
|
|
|
|
else
|
|
|
|
oinfo->has_unresolved = FALSE;
|
|
|
|
}
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
void
|
|
|
|
_clutter_script_apply_properties (ClutterScript *script,
|
|
|
|
ObjectInfo *oinfo)
|
|
|
|
{
|
|
|
|
ClutterScriptable *scriptable = NULL;
|
|
|
|
ClutterScriptableIface *iface = NULL;
|
|
|
|
gboolean set_custom_property = FALSE;
|
2009-11-04 14:05:13 +00:00
|
|
|
GObject *object = oinfo->object;
|
2009-11-04 13:32:26 +00:00
|
|
|
GList *properties;
|
2009-11-04 14:05:13 +00:00
|
|
|
GArray *params;
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
if (!oinfo->has_unresolved)
|
|
|
|
return;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
/* shortcut, to avoid typechecking every time */
|
|
|
|
if (CLUTTER_IS_SCRIPTABLE (object))
|
|
|
|
{
|
|
|
|
scriptable = CLUTTER_SCRIPTABLE (object);
|
|
|
|
iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
|
|
|
|
|
|
|
|
if (iface->set_custom_property)
|
|
|
|
set_custom_property = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* then we get the rest of the parameters, asking the object itself
|
|
|
|
* to translate them for us, if we cannot do that
|
|
|
|
*/
|
|
|
|
properties = oinfo->properties;
|
|
|
|
oinfo->properties = clutter_script_translate_parameters (script,
|
|
|
|
object,
|
|
|
|
oinfo->id,
|
|
|
|
properties,
|
|
|
|
¶ms);
|
|
|
|
|
|
|
|
/* consume all the properties we could translate in this pass */
|
|
|
|
for (i = 0; i < params->len; i++)
|
|
|
|
{
|
|
|
|
GParameter *param = &g_array_index (params, GParameter, i);
|
|
|
|
|
|
|
|
CLUTTER_NOTE (SCRIPT,
|
|
|
|
"Setting %s property '%s' (type:%s) to object '%s' (id:%s)",
|
|
|
|
set_custom_property ? "custom" : "regular",
|
|
|
|
param->name,
|
|
|
|
g_type_name (G_VALUE_TYPE (¶m->value)),
|
|
|
|
g_type_name (oinfo->gtype),
|
|
|
|
oinfo->id);
|
|
|
|
|
|
|
|
if (set_custom_property)
|
|
|
|
iface->set_custom_property (scriptable, script,
|
|
|
|
param->name,
|
|
|
|
¶m->value);
|
|
|
|
else
|
|
|
|
g_object_set_property (object, param->name, ¶m->value);
|
|
|
|
|
|
|
|
g_free ((gchar *) param->name);
|
|
|
|
g_value_unset (¶m->value);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_free (params, TRUE);
|
2009-11-04 14:05:13 +00:00
|
|
|
|
|
|
|
_clutter_script_check_unresolved (script, oinfo);
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
|
2009-11-04 14:05:13 +00:00
|
|
|
void
|
2009-11-04 13:32:26 +00:00
|
|
|
_clutter_script_construct_object (ClutterScript *script,
|
|
|
|
ObjectInfo *oinfo)
|
|
|
|
{
|
2011-10-11 22:42:23 +00:00
|
|
|
GArray *params = NULL;
|
2009-11-04 14:05:13 +00:00
|
|
|
guint i;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
/* we have completely updated the object */
|
2009-11-04 14:05:13 +00:00
|
|
|
if (oinfo->object != NULL)
|
|
|
|
{
|
|
|
|
if (oinfo->has_unresolved)
|
|
|
|
_clutter_script_check_unresolved (script, oinfo);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
if (oinfo->gtype == G_TYPE_INVALID)
|
|
|
|
{
|
|
|
|
if (G_UNLIKELY (oinfo->type_func))
|
2011-09-07 15:14:10 +00:00
|
|
|
oinfo->gtype = _clutter_script_get_type_from_symbol (oinfo->type_func);
|
2009-11-04 13:32:26 +00:00
|
|
|
else
|
|
|
|
oinfo->gtype = clutter_script_get_type_from_name (script, oinfo->class_name);
|
|
|
|
|
|
|
|
if (G_UNLIKELY (oinfo->gtype == G_TYPE_INVALID))
|
2009-11-04 14:05:13 +00:00
|
|
|
return;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
script: Fix the memory management
Currently, the memory management in ClutterScript is overly complicated.
The basic design tenet should be:
- ClutterScript owns a reference on every object it creates
This allows the Script instance to reliably handle the lifetime of the
instances from creation to disposal.
In case of unmerge, the Script instance should destroy any Actor
instance, except for the Stage, and release the reference it owns. The
Stage is special because it's really owned by Clutter itself, and it
should be destroyed explicitly.
When disposing the Script itself, it should just release the reference;
any parented actor, or any InitiallyUnowned instance, will then be
managed by the parent object, as they should, while every GObject
instance will go away, as documented.
This commit is based on a patch by:
Henrik Hedberg <hhedberg@innologies.fi>
http://bugzilla.clutter-project.org/show_bug.cgi?id=2316
2010-09-13 20:29:52 +00:00
|
|
|
oinfo->is_actor = g_type_is_a (oinfo->gtype, CLUTTER_TYPE_ACTOR);
|
|
|
|
if (oinfo->is_actor)
|
|
|
|
oinfo->is_stage = g_type_is_a (oinfo->gtype, CLUTTER_TYPE_STAGE);
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|
|
|
|
|
script: Fix the memory management
Currently, the memory management in ClutterScript is overly complicated.
The basic design tenet should be:
- ClutterScript owns a reference on every object it creates
This allows the Script instance to reliably handle the lifetime of the
instances from creation to disposal.
In case of unmerge, the Script instance should destroy any Actor
instance, except for the Stage, and release the reference it owns. The
Stage is special because it's really owned by Clutter itself, and it
should be destroyed explicitly.
When disposing the Script itself, it should just release the reference;
any parented actor, or any InitiallyUnowned instance, will then be
managed by the parent object, as they should, while every GObject
instance will go away, as documented.
This commit is based on a patch by:
Henrik Hedberg <hhedberg@innologies.fi>
http://bugzilla.clutter-project.org/show_bug.cgi?id=2316
2010-09-13 20:29:52 +00:00
|
|
|
if (oinfo->is_stage && oinfo->is_stage_default)
|
2009-11-04 13:32:26 +00:00
|
|
|
{
|
2011-11-09 17:09:37 +00:00
|
|
|
ClutterStageManager *manager = clutter_stage_manager_get_default ();
|
2009-11-04 13:32:26 +00:00
|
|
|
GList *properties = oinfo->properties;
|
2011-11-09 17:09:37 +00:00
|
|
|
ClutterStage *default_stage;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
/* the default stage is a complex beast: we cannot create it using
|
|
|
|
* g_object_newv() but we need clutter_script_construct_parameters()
|
|
|
|
* to add the GParamSpec to the PropertyInfo pspec member, so
|
|
|
|
* that we don't have to implement every complex property (like
|
|
|
|
* the "color" one) directly inside the ClutterStage class.
|
|
|
|
*/
|
|
|
|
oinfo->properties =
|
|
|
|
clutter_script_construct_parameters (script,
|
|
|
|
oinfo->gtype,
|
|
|
|
oinfo->id,
|
|
|
|
properties,
|
|
|
|
¶ms);
|
|
|
|
|
2011-11-09 17:09:37 +00:00
|
|
|
default_stage = clutter_stage_manager_get_default_stage (manager);
|
|
|
|
oinfo->object = G_OBJECT (default_stage);
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
for (i = 0; i < params->len; i++)
|
|
|
|
{
|
|
|
|
GParameter *param = &g_array_index (params, GParameter, i);
|
|
|
|
|
|
|
|
g_free ((gchar *) param->name);
|
|
|
|
g_value_unset (¶m->value);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_free (params, TRUE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GList *properties = oinfo->properties;
|
2012-01-17 14:17:20 +00:00
|
|
|
GParameter *parameters;
|
2009-11-04 13:32:26 +00:00
|
|
|
|
|
|
|
/* every other object: first, we get the construction parameters */
|
|
|
|
oinfo->properties =
|
|
|
|
clutter_script_construct_parameters (script,
|
|
|
|
oinfo->gtype,
|
|
|
|
oinfo->id,
|
|
|
|
properties,
|
|
|
|
¶ms);
|
|
|
|
|
2012-01-17 14:17:20 +00:00
|
|
|
parameters = (GParameter *) (void *) params->data;
|
2009-11-04 14:05:13 +00:00
|
|
|
oinfo->object = g_object_newv (oinfo->gtype,
|
|
|
|
params->len,
|
2012-01-16 23:49:49 +00:00
|
|
|
parameters);
|
2009-11-04 13:32:26 +00:00
|
|
|
|
script: Fix the memory management
Currently, the memory management in ClutterScript is overly complicated.
The basic design tenet should be:
- ClutterScript owns a reference on every object it creates
This allows the Script instance to reliably handle the lifetime of the
instances from creation to disposal.
In case of unmerge, the Script instance should destroy any Actor
instance, except for the Stage, and release the reference it owns. The
Stage is special because it's really owned by Clutter itself, and it
should be destroyed explicitly.
When disposing the Script itself, it should just release the reference;
any parented actor, or any InitiallyUnowned instance, will then be
managed by the parent object, as they should, while every GObject
instance will go away, as documented.
This commit is based on a patch by:
Henrik Hedberg <hhedberg@innologies.fi>
http://bugzilla.clutter-project.org/show_bug.cgi?id=2316
2010-09-13 20:29:52 +00:00
|
|
|
/* by sinking the floating reference, we make sure that the reference
|
|
|
|
* count is correct whether the object is referenced from somewhere
|
|
|
|
* else too or only by this ClutterScript object.
|
|
|
|
*/
|
|
|
|
g_object_ref_sink (oinfo->object);
|
|
|
|
|
2009-11-04 13:32:26 +00:00
|
|
|
for (i = 0; i < params->len; i++)
|
|
|
|
{
|
|
|
|
GParameter *param = &g_array_index (params, GParameter, i);
|
|
|
|
|
|
|
|
g_free ((gchar *) param->name);
|
|
|
|
g_value_unset (¶m->value);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_free (params, TRUE);
|
|
|
|
}
|
|
|
|
|
2009-11-04 14:05:13 +00:00
|
|
|
g_assert (oinfo->object != NULL);
|
2009-11-04 13:32:26 +00:00
|
|
|
|
2009-11-04 14:05:13 +00:00
|
|
|
if (CLUTTER_IS_SCRIPTABLE (oinfo->object))
|
|
|
|
clutter_scriptable_set_id (CLUTTER_SCRIPTABLE (oinfo->object), oinfo->id);
|
2009-11-04 13:32:26 +00:00
|
|
|
else
|
2009-11-04 14:05:13 +00:00
|
|
|
g_object_set_data_full (oinfo->object, "clutter-script-id",
|
2009-11-04 13:32:26 +00:00
|
|
|
g_strdup (oinfo->id),
|
|
|
|
g_free);
|
|
|
|
|
2009-11-04 14:05:13 +00:00
|
|
|
_clutter_script_check_unresolved (script, oinfo);
|
2009-11-04 13:32:26 +00:00
|
|
|
}
|