Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-script-parser.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
7  * Copyright (C) 2009 Intel Corportation
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
21  *
22  * Original author:
23  *
24  *      Emmanuele Bassi <ebassi@linux.intel.com>
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <string.h>
32 #include <stdlib.h>
33
34 #include <glib.h>
35 #include <gmodule.h>
36
37 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
38 #include "deprecated/clutter-container.h"
39 #include "deprecated/clutter-alpha.h"
40
41 #include "clutter-actor.h"
42 #include "clutter-debug.h"
43 #include "clutter-enum-types.h"
44
45 #include "clutter-script.h"
46 #include "clutter-script-private.h"
47 #include "clutter-scriptable.h"
48
49 #include "clutter-stage-manager.h"
50
51 #include "clutter-private.h"
52
53 static void clutter_script_parser_object_end (JsonParser *parser,
54                                               JsonObject *object);
55 static void clutter_script_parser_parse_end  (JsonParser *parser);
56
57 #define clutter_script_parser_get_type  _clutter_script_parser_get_type
58
59 G_DEFINE_TYPE (ClutterScriptParser, clutter_script_parser, JSON_TYPE_PARSER);
60
61 static void
62 clutter_script_parser_class_init (ClutterScriptParserClass *klass)
63 {
64   JsonParserClass *parser_class = JSON_PARSER_CLASS (klass);
65
66   parser_class->object_end = clutter_script_parser_object_end;
67   parser_class->parse_end = clutter_script_parser_parse_end;
68 }
69
70 static void
71 clutter_script_parser_init (ClutterScriptParser *parser)
72 {
73 }
74
75 GType
76 _clutter_script_get_type_from_symbol (const gchar *symbol)
77 {
78   static GModule *module = NULL;
79   GTypeGetFunc func;
80   GType gtype = G_TYPE_INVALID;
81
82   if (!module)
83     module = g_module_open (NULL, 0);
84   
85   if (g_module_symbol (module, symbol, (gpointer)&func))
86     gtype = func ();
87   
88   return gtype;
89 }
90
91 GType
92 _clutter_script_get_type_from_class (const gchar *name)
93 {
94   static GModule *module = NULL;
95   GString *symbol_name = g_string_sized_new (64);
96   GType gtype = G_TYPE_INVALID;
97   GTypeGetFunc func;
98   gchar *symbol;
99   gint i;
100
101   if (G_UNLIKELY (!module))
102     module = g_module_open (NULL, 0);
103   
104   for (i = 0; name[i] != '\0'; i++)
105     {
106       gchar c = name[i];
107
108       /* the standard naming policy for GObject-based libraries
109        * is:
110        *
111        *   NAME := INITIAL_WORD WORD+
112        *   INITIAL_WORD := [A-Z][a-z0-9]*
113        *   WORD := [A-Z]{1,2}[a-z0-9]+ | [A-Z]{2,}
114        *
115        * for instance:
116        *
117        *   GString -> g_string
118        *   GtkCTree -> gtk_ctree
119        *   ClutterX11TexturePixmap -> clutter_x11_texture_pixmap
120        *
121        * see:
122        *
123        * http://mail.gnome.org/archives/gtk-devel-list/2007-June/msg00022.html
124        *
125        * and:
126        *
127        * http://git.gnome.org/cgit/gtk+/plain/gtk/gtkbuilderparser.c
128        */
129
130       if ((c == g_ascii_toupper (c) &&
131            i > 0 && name[i - 1] != g_ascii_toupper (name[i - 1])) ||
132           (i > 2 && name[i] == g_ascii_toupper (name[i]) &&
133            name[i - 1] == g_ascii_toupper (name[i - 1]) &&
134            name[i - 2] == g_ascii_toupper (name[i - 2])))
135         g_string_append_c (symbol_name, '_');
136
137       g_string_append_c (symbol_name, g_ascii_tolower (c));
138     }
139
140   g_string_append (symbol_name, "_get_type");
141   
142   symbol = g_string_free (symbol_name, FALSE);
143
144   if (g_module_symbol (module, symbol, (gpointer)&func))
145     {
146       CLUTTER_NOTE (SCRIPT, "Type function: %s", symbol);
147       gtype = func ();
148     }
149   
150   g_free (symbol);
151
152   return gtype;
153 }
154
155 /*
156  * clutter_script_enum_from_string:
157  * @type: a #GType for an enumeration type
158  * @string: the enumeration value as a string
159  * @enum_value: (out): return location for the enumeration value as an integer
160  *
161  * Converts an enumeration value inside @string into a numeric
162  * value and places it into @enum_value.
163  *
164  * The enumeration value can be an integer, the enumeration nick
165  * or the enumeration name, as part of the #GEnumValue structure.
166  *
167  * Return value: %TRUE if the conversion was successfull.
168  */
169 gboolean
170 _clutter_script_enum_from_string (GType        type,
171                                   const gchar *string,
172                                   gint        *enum_value)
173 {
174   GEnumClass *eclass;
175   GEnumValue *ev;
176   gchar *endptr;
177   gint value;
178   gboolean retval = TRUE;
179   
180   g_return_val_if_fail (G_TYPE_IS_ENUM (type), 0);
181   g_return_val_if_fail (string != NULL, 0);
182   
183   value = strtoul (string, &endptr, 0);
184   if (endptr != string) /* parsed a number */
185     *enum_value = value;
186   else
187     {
188       eclass = g_type_class_ref (type);
189       ev = g_enum_get_value_by_name (eclass, string);
190       if (!ev)
191         ev = g_enum_get_value_by_nick (eclass, string);
192
193       if (ev)
194         *enum_value = ev->value;
195       else
196         retval = FALSE;
197       
198       g_type_class_unref (eclass);
199     }
200
201   return retval;
202 }
203
204 gboolean
205 _clutter_script_flags_from_string (GType        type,
206                                    const gchar *string,
207                                    gint        *flags_value)
208 {
209   gchar *endptr, *prevptr;
210   guint i, j, ret, value;
211   gchar *flagstr;
212   GFlagsValue *fv;
213   const gchar *flag;
214
215   g_return_val_if_fail (G_TYPE_IS_FLAGS (type), 0);
216   g_return_val_if_fail (string != NULL, 0);
217
218   ret = TRUE;
219   
220   value = strtoul (string, &endptr, 0);
221   if (endptr != string) /* parsed a number */
222     *flags_value = value;
223   else
224     {
225       GFlagsClass *fclass;
226
227       fclass = g_type_class_ref (type);
228
229       flagstr = g_strdup (string);
230       for (value = i = j = 0; ; i++)
231         {
232           gboolean eos = (flagstr[i] == '\0') ? TRUE : FALSE;
233           
234           if (!eos && flagstr[i] != '|')
235             continue;
236           
237           flag = &flagstr[j];
238           endptr = &flagstr[i];
239           
240           if (!eos)
241             {
242               flagstr[i++] = '\0';
243               j = i;
244             }
245           
246           /* trim spaces */
247           for (;;)
248             {
249               gunichar ch = g_utf8_get_char (flag);
250               if (!g_unichar_isspace (ch))
251                 break;
252
253               flag = g_utf8_next_char (flag);
254             }
255           
256           while (endptr > flag)
257             {
258               gunichar ch;
259
260               prevptr = g_utf8_prev_char (endptr);
261
262               ch = g_utf8_get_char (prevptr);
263               if (!g_unichar_isspace (ch))
264                 break;
265
266               endptr = prevptr;
267             }
268           
269           if (endptr > flag)
270             {
271               *endptr = '\0';
272
273               fv = g_flags_get_value_by_name (fclass, flag);
274               
275               if (!fv)
276                 fv = g_flags_get_value_by_nick (fclass, flag);
277               
278               if (fv)
279                 value |= fv->value;
280               else
281                 {
282                   ret = FALSE;
283                   break;
284                 }
285             }
286           
287           if (eos)
288             {
289               *flags_value = value;
290               break;
291             }
292         }
293       
294       g_free (flagstr);
295       
296       g_type_class_unref (fclass);
297     }
298
299   return ret;
300 }
301
302 static gboolean
303 parse_knot_from_array (JsonArray   *array,
304                        ClutterKnot *knot)
305 {
306   if (json_array_get_length (array) != 2)
307     return FALSE;
308
309   knot->x = json_array_get_int_element (array, 0);
310   knot->y = json_array_get_int_element (array, 1);
311
312   return TRUE;
313 }
314
315 static gboolean
316 parse_knot_from_object (JsonObject  *object,
317                         ClutterKnot *knot)
318 {
319   if (json_object_has_member (object, "x"))
320     knot->x = json_object_get_int_member (object, "x");
321   else
322     knot->x = 0;
323
324   if (json_object_has_member (object, "y"))
325     knot->y = json_object_get_int_member (object, "y");
326   else
327     knot->y = 0;
328
329   return TRUE;
330 }
331
332 gboolean
333 _clutter_script_parse_knot (ClutterScript *script,
334                             JsonNode      *node,
335                             ClutterKnot   *knot)
336 {
337   g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
338   g_return_val_if_fail (node != NULL, FALSE);
339   g_return_val_if_fail (knot != NULL, FALSE);
340
341   switch (JSON_NODE_TYPE (node))
342     {
343     case JSON_NODE_ARRAY:
344       return parse_knot_from_array (json_node_get_array (node), knot);
345
346     case JSON_NODE_OBJECT:
347       return parse_knot_from_object (json_node_get_object (node), knot);
348
349     default:
350       break;
351     }
352
353   return FALSE;
354 }
355
356 static gboolean
357 parse_geometry_from_array (JsonArray       *array,
358                            ClutterGeometry *geometry)
359 {
360   if (json_array_get_length (array) != 4)
361     return FALSE;
362
363   geometry->x = json_array_get_int_element (array, 0);
364   geometry->y = json_array_get_int_element (array, 1);
365   geometry->width = json_array_get_int_element (array, 2);
366   geometry->height = json_array_get_int_element (array, 3);
367
368   return TRUE;
369 }
370
371 static gboolean
372 parse_geometry_from_object (JsonObject      *object,
373                             ClutterGeometry *geometry)
374 {
375   if (json_object_has_member (object, "x"))
376     geometry->x = json_object_get_int_member (object, "x");
377   else
378     geometry->x = 0;
379
380   if (json_object_has_member (object, "y"))
381     geometry->y = json_object_get_int_member (object, "y");
382   else
383     geometry->y = 0;
384
385   if (json_object_has_member (object, "width"))
386     geometry->width = json_object_get_int_member (object, "width");
387   else
388     geometry->width = 0;
389
390   if (json_object_has_member (object, "height"))
391     geometry->height = json_object_get_int_member (object, "height");
392   else
393     geometry->height = 0;
394
395   return TRUE;
396 }
397
398 gboolean
399 _clutter_script_parse_geometry (ClutterScript   *script,
400                                 JsonNode        *node,
401                                 ClutterGeometry *geometry)
402 {
403   g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
404   g_return_val_if_fail (node != NULL, FALSE);
405   g_return_val_if_fail (geometry != NULL, FALSE);
406
407   switch (JSON_NODE_TYPE (node))
408     {
409     case JSON_NODE_ARRAY:
410       return parse_geometry_from_array (json_node_get_array (node), geometry);
411
412     case JSON_NODE_OBJECT:
413       return parse_geometry_from_object (json_node_get_object (node), geometry);
414
415     default:
416       break;
417     }
418
419   return FALSE;
420 }
421
422 static gboolean
423 parse_color_from_array (JsonArray    *array,
424                         ClutterColor *color)
425 {
426   if (json_array_get_length (array) != 3 ||
427       json_array_get_length (array) != 4)
428     return FALSE;
429
430   color->red   = CLAMP (json_array_get_int_element (array, 0), 0, 255);
431   color->green = CLAMP (json_array_get_int_element (array, 1), 0, 255);
432   color->blue  = CLAMP (json_array_get_int_element (array, 2), 0, 255);
433
434   if (json_array_get_length (array) == 4)
435     color->alpha = CLAMP (json_array_get_int_element (array, 3), 0, 255);
436   else
437     color->alpha = 255;
438
439   return TRUE;
440 }
441
442 static gboolean
443 parse_color_from_object (JsonObject   *object,
444                          ClutterColor *color)
445 {
446   if (json_object_has_member (object, "red"))
447     color->red = CLAMP (json_object_get_int_member (object, "red"), 0, 255);
448   else
449     color->red = 0;
450
451   if (json_object_has_member (object, "green"))
452     color->green = CLAMP (json_object_get_int_member (object, "green"), 0, 255);
453   else
454     color->green = 0;
455
456   if (json_object_has_member (object, "blue"))
457     color->blue = CLAMP (json_object_get_int_member (object, "blue"), 0, 255);
458   else
459     color->blue = 0;
460
461   if (json_object_has_member (object, "alpha"))
462     color->alpha = CLAMP (json_object_get_int_member (object, "alpha"), 0, 255);
463   else
464     color->alpha = 255;
465
466   return TRUE;
467 }
468
469 gboolean
470 _clutter_script_parse_color (ClutterScript *script,
471                              JsonNode      *node,
472                              ClutterColor  *color)
473 {
474   g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
475   g_return_val_if_fail (node != NULL, FALSE);
476   g_return_val_if_fail (color != NULL, FALSE);
477
478   switch (JSON_NODE_TYPE (node))
479     {
480     case JSON_NODE_ARRAY:
481       return parse_color_from_array (json_node_get_array (node), color);
482
483     case JSON_NODE_OBJECT:
484       return parse_color_from_object (json_node_get_object (node), color);
485
486     case JSON_NODE_VALUE:
487       return clutter_color_from_string (color, json_node_get_string (node));
488
489     default:
490       break;
491     }
492
493   return FALSE;
494 }
495
496 static gboolean
497 parse_point_from_array (JsonArray    *array,
498                         ClutterPoint *point)
499 {
500   if (json_array_get_length (array) != 2)
501     return FALSE;
502
503   point->x = json_array_get_double_element (array, 0);
504   point->y = json_array_get_double_element (array, 1);
505
506   return TRUE;
507 }
508
509 static gboolean
510 parse_point_from_object (JsonObject   *object,
511                          ClutterPoint *point)
512 {
513   if (json_object_has_member (object, "x"))
514     point->x = json_object_get_double_member (object, "x");
515   else
516     point->x = 0.f;
517
518   if (json_object_has_member (object, "y"))
519     point->y = json_object_get_double_member (object, "y");
520   else
521     point->y = 0.f;
522
523   return TRUE;
524 }
525
526 gboolean
527 _clutter_script_parse_point (ClutterScript *script,
528                              JsonNode      *node,
529                              ClutterPoint  *point)
530 {
531   g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
532   g_return_val_if_fail (node != NULL, FALSE);
533   g_return_val_if_fail (point != NULL, FALSE);
534
535   switch (JSON_NODE_TYPE (node))
536     {
537     case JSON_NODE_ARRAY:
538       return parse_point_from_array (json_node_get_array (node), point);
539
540     case JSON_NODE_OBJECT:
541       return parse_point_from_object (json_node_get_object (node), point);
542
543     default:
544       break;
545     }
546
547   return FALSE;
548 }
549
550 static gboolean
551 parse_size_from_array (JsonArray   *array,
552                        ClutterSize *size)
553 {
554   if (json_array_get_length (array) != 2)
555     return FALSE;
556
557   size->width = json_array_get_double_element (array, 0);
558   size->height = json_array_get_double_element (array, 1);
559
560   return TRUE;
561 }
562
563 static gboolean
564 parse_size_from_object (JsonObject  *object,
565                         ClutterSize *size)
566 {
567   if (json_object_has_member (object, "width"))
568     size->width = json_object_get_double_member (object, "width");
569   else
570     size->width = 0.f;
571
572   if (json_object_has_member (object, "height"))
573     size->height = json_object_get_double_member (object, "height");
574   else
575     size->height = 0.f;
576
577   return TRUE;
578 }
579
580 gboolean
581 _clutter_script_parse_size (ClutterScript *script,
582                             JsonNode      *node,
583                             ClutterSize   *size)
584 {
585   g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
586   g_return_val_if_fail (node != NULL, FALSE);
587   g_return_val_if_fail (size != NULL, FALSE);
588
589   switch (JSON_NODE_TYPE (node))
590     {
591     case JSON_NODE_ARRAY:
592       return parse_size_from_array (json_node_get_array (node), size);
593
594     case JSON_NODE_OBJECT:
595       return parse_size_from_object (json_node_get_object (node), size);
596
597     default:
598       break;
599     }
600
601   return FALSE;
602 }
603
604 const gchar *
605 _clutter_script_get_id_from_node (JsonNode *node)
606 {
607   JsonObject *object;
608
609   switch (JSON_NODE_TYPE (node))
610     {
611     case JSON_NODE_OBJECT:
612       object = json_node_get_object (node);
613       if (json_object_has_member (object, "id"))
614         return json_object_get_string_member (object, "id");
615       break;
616
617     case JSON_NODE_VALUE:
618       return json_node_get_string (node);
619
620     default:
621       break;
622     }
623
624   return NULL;
625 }
626
627 static GList *
628 parse_children (ObjectInfo *oinfo,
629                 JsonNode   *node)
630 {
631   JsonArray *array;
632   GList *retval;
633   guint array_len, i;
634
635   if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
636     return NULL;
637
638   retval = oinfo->children;
639
640   array = json_node_get_array (node);
641   array_len = json_array_get_length (array);
642
643   for (i = 0; i < array_len; i++)
644     {
645       JsonNode *child = json_array_get_element (array, i);
646       const gchar *id_;
647
648       id_ = _clutter_script_get_id_from_node (child);
649       if (id_ != NULL)
650         retval = g_list_prepend (retval, g_strdup (id_));
651     }
652
653   return g_list_reverse (retval);
654 }
655
656 static GList *
657 parse_signals (ClutterScript *script,
658                ObjectInfo    *oinfo,
659                JsonNode      *node)
660 {
661   JsonArray *array;
662   GList *retval;
663   guint array_len, i;
664
665   if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
666     {
667       _clutter_script_warn_invalid_value (script, "signals", "Array", node);
668       return NULL;
669     }
670
671   retval = oinfo->signals;
672   array = json_node_get_array (node);
673   array_len = json_array_get_length (array);
674
675   for (i = 0; i < array_len; i++)
676     {
677       JsonNode *val = json_array_get_element (array, i);
678       SignalInfo *sinfo = NULL;
679       JsonObject *object;
680       const gchar *name;
681
682       if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT)
683         {
684           _clutter_script_warn_invalid_value (script,
685                                               "signals array", "Object",
686                                               node);
687           continue;
688         }
689
690       object = json_node_get_object (val);
691
692       /* mandatory: "name" */
693       if (!json_object_has_member (object, "name"))
694         {
695           _clutter_script_warn_missing_attribute (script, NULL, "name");
696           continue;
697         }
698       else
699         {
700           name = json_object_get_string_member (object, "name");
701           if (!name)
702             {
703               _clutter_script_warn_invalid_value (script,
704                                                   "name", "string",
705                                                   val);
706               continue;
707             }
708         }
709
710       /* mandatory: "target-state" or "handler" */
711       if (json_object_has_member (object, "target-state"))
712         {
713           const gchar *state = NULL;
714           const gchar *target = NULL;
715           gboolean warp_to = FALSE;
716
717           target = json_object_get_string_member (object, "target-state");
718           if (target == NULL)
719             {
720               _clutter_script_warn_invalid_value (script,
721                                                   "target-state", "string",
722                                                   val);
723               continue;
724             }
725
726           if (json_object_has_member (object, "states"))
727             state = json_object_get_string_member (object, "states");
728
729           if (json_object_has_member (object, "warp"))
730             warp_to = json_object_get_boolean_member (object, "warp");
731
732           CLUTTER_NOTE (SCRIPT,
733                         "Added signal '%s' (states:%s, target-state:%s, warp:%s)",
734                         name,
735                         state != NULL ? state : "<default>", target,
736                         warp_to ? "true" : "false");
737
738           sinfo = g_slice_new0 (SignalInfo);
739           sinfo->is_handler = FALSE;
740           sinfo->name = g_strdup (name);
741           sinfo->state = g_strdup (state);
742           sinfo->target = g_strdup (target);
743           sinfo->warp_to = warp_to;
744         }
745       else if (json_object_has_member (object, "handler"))
746         {
747           const gchar *handler;
748           const gchar *connect;
749           GConnectFlags flags = 0;
750
751           handler = json_object_get_string_member (object, "handler");
752           if (handler == NULL)
753             {
754               _clutter_script_warn_invalid_value (script,
755                                                   "handler", "string",
756                                                   val);
757               continue;
758             }
759
760           /* optional: "object" */
761           if (json_object_has_member (object, "object"))
762             connect = json_object_get_string_member (object, "object");
763           else
764             connect = NULL;
765
766           /* optional: "after" */
767           if (json_object_has_member (object, "after"))
768             {
769               if (json_object_get_boolean_member (object, "after"))
770                 flags |= G_CONNECT_AFTER;
771             }
772
773           /* optional: "swapped" */
774           if (json_object_has_member (object, "swapped"))
775             {
776               if (json_object_get_boolean_member (object, "swapped"))
777                 flags |= G_CONNECT_SWAPPED;
778             }
779
780           CLUTTER_NOTE (SCRIPT,
781                         "Added signal '%s' (handler:%s, object:%s, flags:%d)",
782                         name,
783                         handler, connect, flags);
784
785           sinfo = g_slice_new0 (SignalInfo);
786           sinfo->is_handler = TRUE;
787           sinfo->name = g_strdup (name);
788           sinfo->handler = g_strdup (handler);
789           sinfo->object = g_strdup (connect);
790           sinfo->flags = flags;
791         }
792       else
793         _clutter_script_warn_missing_attribute (script,
794                                                 NULL,
795                                                 "handler or state");
796       if (sinfo != NULL)
797         retval = g_list_prepend (retval, sinfo);
798     }
799
800   return retval;
801 }
802
803 static ClutterTimeline *
804 construct_timeline (ClutterScript *script,
805                     JsonObject    *object)
806 {
807   ClutterTimeline *retval = NULL;
808   ObjectInfo *oinfo;
809   GList *members, *l;
810
811   /* we fake an ObjectInfo so we can reuse clutter_script_construct_object()
812    * here; we do not save it inside the hash table, because if this had
813    * been a named object then we wouldn't have ended up here in the first
814    * place
815    */
816   oinfo = g_slice_new0 (ObjectInfo);
817   oinfo->gtype = CLUTTER_TYPE_TIMELINE;
818   oinfo->id = g_strdup ("dummy");
819
820   members = json_object_get_members (object);
821   for (l = members; l != NULL; l = l->next)
822     {
823       const gchar *name = l->data;
824       JsonNode *node = json_object_get_member (object, name);
825       PropertyInfo *pinfo = g_slice_new0 (PropertyInfo);
826
827       pinfo->name = g_strdelimit (g_strdup (name), G_STR_DELIMITERS, '-');
828       pinfo->node = json_node_copy (node);
829
830       oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
831     }
832
833   g_list_free (members);
834
835   _clutter_script_construct_object (script, oinfo);
836   _clutter_script_apply_properties (script, oinfo);
837   retval = CLUTTER_TIMELINE (oinfo->object);
838
839   /* we transfer ownership to the alpha function, so we ref before
840    * destroying the ObjectInfo to avoid the timeline going away
841    */
842   g_object_ref (retval);
843   object_info_free (oinfo);
844
845   return retval;
846 }
847
848 /* define the names of the animation modes to match the ones
849  * that developers might be more accustomed to
850  */
851 static const struct
852 {
853   const gchar *name;
854   ClutterAnimationMode mode;
855 } animation_modes[] = {
856   { "linear", CLUTTER_LINEAR },
857   { "easeInQuad", CLUTTER_EASE_IN_QUAD },
858   { "easeOutQuad", CLUTTER_EASE_OUT_QUAD },
859   { "easeInOutQuad", CLUTTER_EASE_IN_OUT_QUAD },
860   { "easeInCubic", CLUTTER_EASE_IN_CUBIC },
861   { "easeOutCubic", CLUTTER_EASE_OUT_CUBIC },
862   { "easeInOutCubic", CLUTTER_EASE_IN_OUT_CUBIC },
863   { "easeInQuart", CLUTTER_EASE_IN_QUART },
864   { "easeOutQuart", CLUTTER_EASE_OUT_QUART },
865   { "easeInOutQuart", CLUTTER_EASE_IN_OUT_QUART },
866   { "easeInQuint", CLUTTER_EASE_IN_QUINT },
867   { "easeOutQuint", CLUTTER_EASE_OUT_QUINT },
868   { "easeInOutQuint", CLUTTER_EASE_IN_OUT_QUINT },
869   { "easeInSine", CLUTTER_EASE_IN_SINE },
870   { "easeOutSine", CLUTTER_EASE_OUT_SINE },
871   { "easeInOutSine", CLUTTER_EASE_IN_OUT_SINE },
872   { "easeInExpo", CLUTTER_EASE_IN_EXPO },
873   { "easeOutExpo", CLUTTER_EASE_OUT_EXPO },
874   { "easeInOutExpo", CLUTTER_EASE_IN_OUT_EXPO },
875   { "easeInCirc", CLUTTER_EASE_IN_CIRC },
876   { "easeOutCirc", CLUTTER_EASE_OUT_CIRC },
877   { "easeInOutCirc", CLUTTER_EASE_IN_OUT_CIRC },
878   { "easeInElastic", CLUTTER_EASE_IN_ELASTIC },
879   { "easeOutElastic", CLUTTER_EASE_OUT_ELASTIC },
880   { "easeInOutElastic", CLUTTER_EASE_IN_OUT_ELASTIC },
881   { "easeInBack", CLUTTER_EASE_IN_BACK },
882   { "easeOutBack", CLUTTER_EASE_OUT_BACK },
883   { "easeInOutBack", CLUTTER_EASE_IN_OUT_BACK },
884   { "easeInBounce", CLUTTER_EASE_IN_BOUNCE },
885   { "easeOutBounce", CLUTTER_EASE_OUT_BOUNCE },
886   { "easeInOutBounce", CLUTTER_EASE_IN_OUT_BOUNCE },
887 };
888
889 static const gint n_animation_modes = G_N_ELEMENTS (animation_modes);
890
891 gulong
892 _clutter_script_resolve_animation_mode (JsonNode *node)
893 {
894   gint i, res = CLUTTER_CUSTOM_MODE;
895
896   if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
897     return CLUTTER_CUSTOM_MODE;
898
899   if (json_node_get_value_type (node) == G_TYPE_INT64)
900     return json_node_get_int (node);
901
902   if (json_node_get_value_type (node) == G_TYPE_STRING)
903     {
904       const gchar *name = json_node_get_string (node);
905
906       /* XXX - we might be able to optimize by changing the ordering
907        * of the animation_modes array, e.g.
908        *  - special casing linear
909        *  - tokenizing ('ease', 'In', 'Sine') and matching on token
910        *  - binary searching?
911        */
912       for (i = 0; i < n_animation_modes; i++)
913         {
914           if (strcmp (animation_modes[i].name, name) == 0)
915             return animation_modes[i].mode;
916         }
917
918       if (_clutter_script_enum_from_string (CLUTTER_TYPE_ANIMATION_MODE,
919                                             name,
920                                             &res))
921         return res;
922
923       g_warning ("Unable to find the animation mode '%s'", name);
924     }
925
926   return CLUTTER_CUSTOM_MODE;
927 }
928
929 static ClutterAlphaFunc
930 resolve_alpha_func (const gchar *name)
931 {
932   static GModule *module = NULL;
933   ClutterAlphaFunc func;
934
935   CLUTTER_NOTE (SCRIPT, "Looking up '%s' alpha function", name);
936
937   if (G_UNLIKELY (!module))
938     module = g_module_open (NULL, 0);
939
940   if (g_module_symbol (module, name, (gpointer) &func))
941     {
942       CLUTTER_NOTE (SCRIPT, "Found '%s' alpha function in the symbols table",
943                     name);
944       return func;
945     }
946
947   return NULL;
948 }
949
950 GObject *
951 _clutter_script_parse_alpha (ClutterScript *script,
952                              JsonNode      *node)
953 {
954   GObject *retval = NULL;
955   JsonObject *object;
956   ClutterTimeline *timeline = NULL;
957   ClutterAlphaFunc alpha_func = NULL;
958   ClutterAnimationMode mode = CLUTTER_CUSTOM_MODE;
959   JsonNode *val;
960   gboolean unref_timeline = FALSE;
961
962   if (JSON_NODE_TYPE (node) != JSON_NODE_OBJECT)
963     return NULL;
964
965   object = json_node_get_object (node);
966
967   val = json_object_get_member (object, "timeline");
968   if (val)
969     {
970       if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE &&
971           json_node_get_string (val) != NULL)
972         {
973           const gchar *id_ = json_node_get_string (val);
974
975           timeline =
976             CLUTTER_TIMELINE (clutter_script_get_object (script, id_));
977         }
978       else if (JSON_NODE_TYPE (val) == JSON_NODE_OBJECT)
979         {
980           timeline = construct_timeline (script, json_node_get_object (val));
981           unref_timeline = TRUE;
982         }
983     }
984
985   val = json_object_get_member (object, "mode");
986   if (val != NULL)
987     mode = _clutter_script_resolve_animation_mode (val);
988
989   if (mode == CLUTTER_CUSTOM_MODE)
990     {
991       val = json_object_get_member (object, "function");
992       if (val && json_node_get_string (val) != NULL)
993         {
994           alpha_func = resolve_alpha_func (json_node_get_string (val));
995           if (!alpha_func)
996             {
997               g_warning ("Unable to find the function '%s' in the "
998                          "Clutter alpha functions or the symbols table",
999                          json_node_get_string (val));
1000             }
1001         }
1002     }
1003
1004   CLUTTER_NOTE (SCRIPT, "Parsed alpha: %s timeline (%p) (mode:%d, func:%p)",
1005                 unref_timeline ? "implicit" : "explicit",
1006                 timeline ? timeline : 0x0,
1007                 mode != CLUTTER_CUSTOM_MODE ? mode : 0,
1008                 alpha_func ? alpha_func : 0x0);
1009
1010   retval = g_object_new (CLUTTER_TYPE_ALPHA, NULL);
1011
1012   if (mode != CLUTTER_CUSTOM_MODE)
1013     clutter_alpha_set_mode (CLUTTER_ALPHA (retval), mode);
1014
1015   if (alpha_func != NULL)
1016     clutter_alpha_set_func (CLUTTER_ALPHA (retval), alpha_func, NULL, NULL);
1017
1018   clutter_alpha_set_timeline (CLUTTER_ALPHA (retval), timeline);
1019
1020   /* if we created an implicit timeline, the Alpha has full ownership
1021    * of it now, since it won't be accessible from ClutterScript
1022    */
1023   if (unref_timeline)
1024     g_object_unref (timeline);
1025
1026   return retval;
1027 }
1028
1029 static void
1030 clutter_script_parser_object_end (JsonParser *json_parser,
1031                                   JsonObject *object)
1032 {
1033   ClutterScriptParser *parser = CLUTTER_SCRIPT_PARSER (json_parser);
1034   ClutterScript *script = parser->script;
1035   ObjectInfo *oinfo;
1036   JsonNode *val;
1037   const gchar *id_;
1038   GList *members, *l;
1039
1040   /* if the object definition does not have an 'id' field we'll
1041    * fake one for it...
1042    */
1043   if (!json_object_has_member (object, "id"))
1044     {
1045       gchar *fake;
1046
1047       /* ... unless it doesn't even have a type - in which case
1048        * it is an internal object definition and we're not
1049        * supposed to touch it
1050        */
1051       if (!json_object_has_member (object, "type"))
1052         return;
1053
1054       fake = _clutter_script_generate_fake_id (script);
1055       json_object_set_string_member (object, "id", fake);
1056
1057       CLUTTER_NOTE (SCRIPT,
1058                     "Adding fake id '%s' to object of type '%s'",
1059                     json_object_get_string_member (object, "id"),
1060                     json_object_get_string_member (object, "type"));
1061
1062       g_free (fake);
1063     }
1064
1065   if (!json_object_has_member (object, "type"))
1066     {
1067       val = json_object_get_member (object, "id");
1068
1069       _clutter_script_warn_missing_attribute (script,
1070                                               json_node_get_string (val),
1071                                               "type");
1072       return;
1073     }
1074
1075   id_ = json_object_get_string_member (object, "id");
1076   CLUTTER_NOTE (SCRIPT, "Getting object info for object '%s'", id_);
1077
1078   oinfo = _clutter_script_get_object_info (script, id_);
1079   if (oinfo == NULL)
1080     {
1081       const gchar *class_name;
1082
1083       oinfo = g_slice_new0 (ObjectInfo);
1084       oinfo->merge_id = _clutter_script_get_last_merge_id (script);
1085       oinfo->id = g_strdup (id_);
1086
1087       class_name = json_object_get_string_member (object, "type");
1088       oinfo->class_name = g_strdup (class_name);
1089
1090       if (json_object_has_member (object, "type_func"))
1091         {
1092           const gchar *type_func;
1093
1094           type_func = json_object_get_string_member (object, "type_func");
1095           oinfo->type_func = g_strdup (type_func);
1096
1097           /* remove the type_func member; we don't want it to
1098            * pollute the object members
1099            */
1100           json_object_remove_member (object, "type_func");
1101         }
1102     }
1103
1104   if (json_object_has_member (object, "children"))
1105     {
1106       val = json_object_get_member (object, "children");
1107       oinfo->children = parse_children (oinfo, val);
1108
1109       json_object_remove_member (object, "children");
1110     }
1111
1112   if (json_object_has_member (object, "signals"))
1113     {
1114       val = json_object_get_member (object, "signals");
1115       oinfo->signals = parse_signals (script, oinfo, val);
1116
1117       json_object_remove_member (object, "signals");
1118     }
1119
1120   oinfo->is_actor = FALSE;
1121
1122   if (strcmp (oinfo->class_name, "ClutterStage") == 0 &&
1123       json_object_has_member (object, "is-default"))
1124     {
1125       oinfo->is_actor = TRUE;
1126       oinfo->is_stage = TRUE;
1127       oinfo->is_stage_default =
1128         json_object_get_boolean_member (object, "is-default");
1129
1130       json_object_remove_member (object, "is-default");
1131     }
1132   else
1133     oinfo->is_stage_default = FALSE;
1134
1135   oinfo->is_unmerged = FALSE;
1136   oinfo->has_unresolved = TRUE;
1137
1138   members = json_object_get_members (object);
1139   for (l = members; l; l = l->next)
1140     {
1141       const gchar *name = l->data;
1142       PropertyInfo *pinfo;
1143       JsonNode *node;
1144
1145       CLUTTER_NOTE (SCRIPT, "Object '%s' member '%s'",
1146                     oinfo->id,
1147                     name);
1148
1149       /* we have already parsed these */
1150       if (strcmp (name, "id") == 0 || strcmp (name, "type") == 0)
1151         continue;
1152
1153       node = json_object_get_member (object, name);
1154
1155       /* this should not really happen; we're getting a list of
1156        * member names, and if one does not map a real member
1157        * value then it's likely that something has gone wrong
1158        */
1159       if (G_UNLIKELY (node == NULL))
1160         {
1161           CLUTTER_NOTE (SCRIPT,
1162                         "Empty node for member '%s' of object '%s' (type: %s)",
1163                         name,
1164                         oinfo->id,
1165                         oinfo->class_name);
1166           continue;
1167         }
1168
1169       pinfo = g_slice_new (PropertyInfo);
1170
1171       pinfo->name = g_strdup (name);
1172       pinfo->node = json_node_copy (node);
1173       pinfo->pspec = NULL;
1174       pinfo->is_child = g_str_has_prefix (name, "child::") ? TRUE : FALSE;
1175       pinfo->is_layout = g_str_has_prefix (name, "layout::") ? TRUE : FALSE;
1176
1177       oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
1178     }
1179
1180   g_list_free (members);
1181
1182   CLUTTER_NOTE (SCRIPT,
1183                 "Added object '%s' (type:%s, id:%d, props:%d, signals:%d)",
1184                 oinfo->id,
1185                 oinfo->class_name,
1186                 oinfo->merge_id,
1187                 g_list_length (oinfo->properties),
1188                 g_list_length (oinfo->signals));
1189
1190   _clutter_script_add_object_info (script, oinfo);
1191   _clutter_script_construct_object (script, oinfo);
1192 }
1193
1194 static void
1195 clutter_script_parser_parse_end (JsonParser *parser)
1196 {
1197   clutter_script_ensure_objects (CLUTTER_SCRIPT_PARSER (parser)->script);
1198 }
1199
1200 gboolean
1201 _clutter_script_parse_translatable_string (ClutterScript *script,
1202                                            JsonNode      *node,
1203                                            char         **str)
1204 {
1205   JsonObject *obj;
1206   const char *string, *domain, *context;
1207   const char *res;
1208   gboolean translatable;
1209
1210   if (!JSON_NODE_HOLDS_OBJECT (node))
1211     return FALSE;
1212
1213   obj = json_node_get_object (node);
1214   if (!(json_object_has_member (obj, "translatable") &&
1215         json_object_has_member (obj, "string")))
1216     return FALSE;
1217
1218   translatable = json_object_get_boolean_member (obj, "translatable");
1219
1220   string = json_object_get_string_member (obj, "string");
1221   if (string == NULL || *string == '\0')
1222     return FALSE;
1223
1224   if (json_object_has_member (obj, "context"))
1225     context = json_object_get_string_member (obj, "context");
1226   else
1227     context = NULL;
1228
1229   if (json_object_has_member (obj, "domain"))
1230     domain = json_object_get_string_member (obj, "domain");
1231   else
1232     domain = NULL;
1233
1234   if (domain == NULL || *domain == '\0')
1235     domain = clutter_script_get_translation_domain (script);
1236
1237   if (translatable)
1238     {
1239       if (context != NULL && *context != '\0')
1240         res = g_dpgettext2 (domain, context, string);
1241       else
1242         res = g_dgettext (domain, string);
1243     }
1244   else
1245     res = string;
1246
1247   if (str != NULL)
1248     *str = g_strdup (res);
1249
1250   return TRUE;
1251 }
1252
1253 gboolean
1254 _clutter_script_parse_node (ClutterScript *script,
1255                             GValue        *value,
1256                             const gchar   *name,
1257                             JsonNode      *node,
1258                             GParamSpec    *pspec)
1259 {
1260   GValue node_value = G_VALUE_INIT;
1261   gboolean retval = FALSE;
1262
1263   g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
1264   g_return_val_if_fail (name != NULL, FALSE);
1265   g_return_val_if_fail (node != NULL, FALSE);
1266
1267   switch (JSON_NODE_TYPE (node))
1268     {
1269     case JSON_NODE_OBJECT:
1270       /* if we don't have a GParamSpec we can't infer the type
1271        * of the property; this usually means that this property
1272        * is a custom member that will be parsed by the Scriptable
1273        * interface implementantion
1274        */
1275       if (pspec == NULL && !G_IS_VALUE (value))
1276         return FALSE;
1277       else
1278         {
1279           GType p_type;
1280           ObjectInfo *oinfo;
1281           const gchar *id_;
1282
1283           if (G_IS_VALUE (value))
1284             p_type = G_VALUE_TYPE (value);
1285           else
1286             {
1287               p_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
1288               g_value_init (value, p_type);
1289             }
1290
1291           if (g_type_is_a (p_type, G_TYPE_OBJECT))
1292             {
1293               /* default GObject handling: we get the id and
1294                * retrieve the ObjectInfo for it; since the object
1295                * definitions are parsed leaf-first we are guaranteed
1296                * to have a defined object at this point
1297                */
1298               id_ = _clutter_script_get_id_from_node (node);
1299               if (id_ == NULL || *id_ == '\0')
1300                 return FALSE;
1301
1302               oinfo = _clutter_script_get_object_info (script, id_);
1303               if (oinfo == NULL || oinfo->gtype == G_TYPE_INVALID )
1304                 return FALSE;
1305
1306               if (g_type_is_a (oinfo->gtype, p_type))
1307                 {
1308                   /* force construction, even though it should
1309                    * not be necessary; we don't need the properties
1310                    * to be applied as well: they will when the
1311                    * ScriptParser finishes
1312                    */
1313                   _clutter_script_construct_object (script, oinfo);
1314
1315                   g_value_set_object (value, oinfo->object);
1316
1317                   return TRUE;
1318                 }
1319             }
1320           else if (p_type == CLUTTER_TYPE_KNOT)
1321             {
1322               ClutterKnot knot = { 0, };
1323
1324               /* knot := { "x" : (int), "y" : (int) } */
1325
1326               if (_clutter_script_parse_knot (script, node, &knot))
1327                 {
1328                   g_value_set_boxed (value, &knot);
1329                   return TRUE;
1330                 }
1331             }
1332           else if (p_type == CLUTTER_TYPE_GEOMETRY)
1333             {
1334               ClutterGeometry geom = { 0, };
1335
1336               /* geometry := {
1337                *        "x" : (int),
1338                *        "y" : (int),
1339                *        "width" : (int),
1340                *        "height" : (int)
1341                * }
1342                */
1343
1344               if (_clutter_script_parse_geometry (script, node, &geom))
1345                 {
1346                   g_value_set_boxed (value, &geom);
1347                   return TRUE;
1348                 }
1349             }
1350           else if (p_type == CLUTTER_TYPE_COLOR)
1351             {
1352               ClutterColor color = { 0, };
1353
1354               /* color := {
1355                *        "red" : (int),
1356                *        "green" : (int),
1357                *        "blue" : (int),
1358                *        "alpha" : (int)
1359                * }
1360                */
1361
1362               if (_clutter_script_parse_color (script, node, &color))
1363                 {
1364                   g_value_set_boxed (value, &color);
1365                   return TRUE;
1366                 }
1367             }
1368           else if (p_type == CLUTTER_TYPE_POINT)
1369             {
1370               ClutterPoint point = CLUTTER_POINT_INIT_ZERO;
1371
1372               if (_clutter_script_parse_point (script, node, &point))
1373                 {
1374                   g_value_set_boxed (value, &point);
1375                   return TRUE;
1376                 }
1377             }
1378           else if (p_type == CLUTTER_TYPE_SIZE)
1379             {
1380               ClutterSize size = CLUTTER_SIZE_INIT_ZERO;
1381
1382               if (_clutter_script_parse_size (script, node, &size))
1383                 {
1384                   g_value_set_boxed (value, &size);
1385                   return TRUE;
1386                 }
1387             }
1388           else if (p_type == G_TYPE_STRING)
1389             {
1390               char *str = NULL;
1391
1392               if (_clutter_script_parse_translatable_string (script, node, &str))
1393                 {
1394                   g_value_take_string (value, str);
1395                   return TRUE;
1396                 }
1397             }
1398          }
1399       return FALSE;
1400
1401     case JSON_NODE_ARRAY:
1402       if (!pspec && !G_IS_VALUE (value))
1403         return FALSE;
1404       else
1405         {
1406           if (!G_IS_VALUE (value))
1407             g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1408
1409           if (G_VALUE_HOLDS (value, CLUTTER_TYPE_KNOT))
1410             {
1411               ClutterKnot knot = { 0, };
1412
1413               /* knot := [ (int), (int) ] */
1414
1415               if (_clutter_script_parse_knot (script, node, &knot))
1416                 {
1417                   g_value_set_boxed (value, &knot);
1418                   return TRUE;
1419                 }
1420             }
1421           else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_GEOMETRY))
1422             {
1423               ClutterGeometry geom = { 0, };
1424
1425               /* geometry := [ (int), (int), (int), (int) ] */
1426
1427               if (_clutter_script_parse_geometry (script, node, &geom))
1428                 {
1429                   g_value_set_boxed (value, &geom);
1430                   return TRUE;
1431                 }
1432             }
1433           else if (CLUTTER_VALUE_HOLDS_COLOR (value))
1434             {
1435               ClutterColor color = { 0, };
1436
1437               /* color := [ (int), (int), (int), (int) ] */
1438
1439               if (_clutter_script_parse_color (script, node, &color))
1440                 {
1441                   g_value_set_boxed (value, &color);
1442                   return TRUE;
1443                 }
1444             }
1445           else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_POINT))
1446             {
1447               ClutterPoint point = CLUTTER_POINT_INIT_ZERO;
1448
1449               if (_clutter_script_parse_point (script, node, &point))
1450                 {
1451                   g_value_set_boxed (value, &point);
1452                   return TRUE;
1453                 }
1454             }
1455           else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_SIZE))
1456             {
1457               ClutterSize size = CLUTTER_SIZE_INIT_ZERO;
1458
1459               if (_clutter_script_parse_size (script, node, &size))
1460                 {
1461                   g_value_set_boxed (value, &size);
1462                   return TRUE;
1463                 }
1464             }
1465           else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
1466             {
1467               JsonArray *array = json_node_get_array (node);
1468               guint i, array_len = json_array_get_length (array);
1469               GPtrArray *str_array = g_ptr_array_sized_new (array_len);
1470
1471               /* strv := [ (str), (str), ... ] */
1472
1473               for (i = 0; i < array_len; i++)
1474                 {
1475                   JsonNode *val = json_array_get_element (array, i);
1476
1477                   if (JSON_NODE_TYPE (val) != JSON_NODE_VALUE &&
1478                       json_node_get_string (val) == NULL)
1479                     continue;
1480
1481                   g_ptr_array_add (str_array,
1482                                    (gpointer) json_node_get_string (val));
1483                 }
1484
1485               g_value_set_boxed (value, str_array->pdata);
1486               g_ptr_array_free (str_array, TRUE);
1487
1488               return TRUE;
1489             }
1490         }
1491       return FALSE;
1492
1493     case JSON_NODE_NULL:
1494       return FALSE;
1495
1496     case JSON_NODE_VALUE:
1497       json_node_get_value (node, &node_value);
1498
1499       if (!pspec && !G_IS_VALUE (value))
1500         g_value_init (value, G_VALUE_TYPE (&node_value));
1501       else if (pspec)
1502         g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1503
1504       switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)))
1505         {
1506         /* fundamental JSON types */
1507         case G_TYPE_INT64:
1508         case G_TYPE_DOUBLE:
1509         case G_TYPE_STRING:
1510         case G_TYPE_BOOLEAN:
1511           g_value_copy (&node_value, value);
1512           retval = TRUE;
1513           break;
1514
1515         case G_TYPE_INT:
1516           g_value_set_int (value, g_value_get_int64 (&node_value));
1517           retval = TRUE;
1518           break;
1519
1520         case G_TYPE_UINT:
1521           g_value_set_uint (value, (guint) g_value_get_int64 (&node_value));
1522           retval = TRUE;
1523           break;
1524
1525         case G_TYPE_ULONG:
1526           g_value_set_ulong (value, (gulong) g_value_get_int64 (&node_value));
1527           retval = TRUE;
1528           break;
1529
1530         case G_TYPE_UCHAR:
1531           g_value_set_uchar (value, (guchar) g_value_get_int64 (&node_value));
1532           retval = TRUE;
1533           break;
1534
1535         case G_TYPE_FLOAT:
1536           if (G_VALUE_HOLDS (&node_value, G_TYPE_DOUBLE))
1537             {
1538               g_value_set_float (value, g_value_get_double (&node_value));
1539               retval = TRUE;
1540             }
1541           else if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
1542             {
1543               g_value_set_float (value, g_value_get_int64 (&node_value));
1544               retval = TRUE;
1545             }
1546           break;
1547
1548         case G_TYPE_ENUM:
1549           if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
1550             {
1551               g_value_set_enum (value, g_value_get_int64 (&node_value));
1552               retval = TRUE;
1553             }
1554           else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1555             {
1556               gint enum_value;
1557
1558               retval = _clutter_script_enum_from_string (G_VALUE_TYPE (value),
1559                                                          g_value_get_string (&node_value),
1560                                                          &enum_value);
1561               if (retval)
1562                 g_value_set_enum (value, enum_value);
1563             }
1564           break;
1565
1566         case G_TYPE_FLAGS:
1567           if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
1568             {
1569               g_value_set_flags (value, g_value_get_int64 (&node_value));
1570               retval = TRUE;
1571             }
1572           else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1573             {
1574               gint flags_value;
1575
1576               retval = _clutter_script_flags_from_string (G_VALUE_TYPE (value),
1577                                                           g_value_get_string (&node_value),
1578                                                           &flags_value);
1579               if (retval)
1580                 g_value_set_flags (value, flags_value);
1581             }
1582           break;
1583
1584         case G_TYPE_BOXED:
1585           if (G_VALUE_HOLDS (value, CLUTTER_TYPE_COLOR))
1586             {
1587               ClutterColor color = { 0, };
1588
1589               retval = _clutter_script_parse_color (script, node, &color);
1590               if (retval)
1591                 clutter_value_set_color (value, &color);
1592             }
1593           break;
1594
1595         case G_TYPE_OBJECT:
1596           if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1597             {
1598               const gchar *str = g_value_get_string (&node_value);
1599               GObject *object = clutter_script_get_object (script, str);
1600               if (object)
1601                 {
1602                   CLUTTER_NOTE (SCRIPT,
1603                                 "Assigning '%s' (%s) to property '%s'",
1604                                 str,
1605                                 G_OBJECT_TYPE_NAME (object),
1606                                 name);
1607
1608                   g_value_set_object (value, object);
1609                   retval = TRUE;
1610                 }
1611             }
1612           break;
1613
1614         default:
1615           retval = FALSE;
1616           break;
1617         }
1618
1619       if (G_VALUE_TYPE (value) == G_TYPE_GTYPE &&
1620           G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1621         {
1622           const gchar *str = g_value_get_string (&node_value);
1623           GType type = clutter_script_get_type_from_name (script, str);
1624           g_value_set_gtype (value, type);
1625           retval = TRUE;
1626         }
1627
1628       g_value_unset (&node_value);
1629       break;
1630     }
1631
1632   return retval;
1633 }
1634
1635 static GList *
1636 clutter_script_translate_parameters (ClutterScript  *script,
1637                                      GObject        *object,
1638                                      const gchar    *name,
1639                                      GList          *properties,
1640                                      GArray        **params)
1641 {
1642   ClutterScriptable *scriptable = NULL;
1643   ClutterScriptableIface *iface = NULL;
1644   GList *l, *unparsed;
1645   gboolean parse_custom = FALSE;
1646
1647   *params = g_array_new (FALSE, FALSE, sizeof (GParameter));
1648
1649   if (CLUTTER_IS_SCRIPTABLE (object))
1650     {
1651       scriptable = CLUTTER_SCRIPTABLE (object);
1652       iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
1653
1654       if (iface->parse_custom_node)
1655         parse_custom = TRUE;
1656     }
1657
1658   unparsed = NULL;
1659
1660   for (l = properties; l != NULL; l = l->next)
1661     {
1662       PropertyInfo *pinfo = l->data;
1663       GParameter param = { NULL };
1664       gboolean res = FALSE;
1665
1666       if (pinfo->is_child || pinfo->is_layout)
1667         {
1668           CLUTTER_NOTE (SCRIPT, "Skipping %s property '%s'",
1669                         pinfo->is_child ? "child" : "layout",
1670                         pinfo->name);
1671           unparsed = g_list_prepend (unparsed, pinfo);
1672           continue;
1673         }
1674
1675       CLUTTER_NOTE (SCRIPT, "Parsing %s property (id:%s)",
1676                     pinfo->pspec ? "regular" : "custom",
1677                     pinfo->name);
1678
1679       if (parse_custom)
1680         res = iface->parse_custom_node (scriptable, script, &param.value,
1681                                         pinfo->name,
1682                                         pinfo->node);
1683
1684       if (!res)
1685         res = _clutter_script_parse_node (script, &param.value,
1686                                           pinfo->name,
1687                                           pinfo->node,
1688                                           pinfo->pspec);
1689
1690       if (!res)
1691         {
1692           CLUTTER_NOTE (SCRIPT, "Property '%s' ignored", pinfo->name);
1693           unparsed = g_list_prepend (unparsed, pinfo);
1694           continue;
1695         }
1696
1697       param.name = g_strdup (pinfo->name);
1698
1699       g_array_append_val (*params, param);
1700
1701       property_info_free (pinfo);
1702     }
1703
1704   g_list_free (properties);
1705
1706   return unparsed;
1707 }
1708
1709 static GList *
1710 clutter_script_construct_parameters (ClutterScript  *script,
1711                                      GType           gtype,
1712                                      const gchar    *name,
1713                                      GList          *properties,
1714                                      GArray        **construct_params)
1715 {
1716   GObjectClass *klass;
1717   GList *l, *unparsed;
1718
1719   klass = g_type_class_ref (gtype);
1720   g_assert (klass != NULL);
1721
1722   *construct_params = g_array_new (FALSE, FALSE, sizeof (GParameter));
1723
1724   unparsed = NULL;
1725
1726   for (l = properties; l != NULL; l = l->next)
1727     {
1728       PropertyInfo *pinfo = l->data;
1729       GParameter param = { NULL };
1730       GParamSpec *pspec = NULL;
1731
1732       /* we allow custom property names for classes, so if we
1733        * don't find a corresponding GObject property for this
1734        * class we just skip it and let the class itself deal
1735        * with it later on
1736        */
1737       pspec = g_object_class_find_property (klass, pinfo->name);
1738       if (pspec)
1739         pinfo->pspec = g_param_spec_ref (pspec);
1740       else
1741         {
1742           pinfo->pspec = NULL;
1743           unparsed = g_list_prepend (unparsed, pinfo);
1744           continue;
1745         }
1746
1747       if (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY))
1748         {
1749           unparsed = g_list_prepend (unparsed, pinfo);
1750           continue;
1751         }
1752
1753       param.name = g_strdup (pinfo->name);
1754
1755       if (!_clutter_script_parse_node (script, &param.value,
1756                                        pinfo->name,
1757                                        pinfo->node,
1758                                        pinfo->pspec))
1759         {
1760           unparsed = g_list_prepend (unparsed, pinfo);
1761           continue;
1762         }
1763
1764       g_array_append_val (*construct_params, param);
1765
1766       property_info_free (pinfo);
1767     }
1768
1769   g_list_free (properties);
1770
1771   g_type_class_unref (klass);
1772
1773   return unparsed;
1774 }
1775
1776 static void
1777 apply_layout_properties (ClutterScript    *script,
1778                          ClutterContainer *container,
1779                          ClutterActor     *actor,
1780                          ObjectInfo       *oinfo)
1781 {
1782   ClutterScriptable *scriptable = NULL;
1783   ClutterScriptableIface *iface = NULL;
1784   gboolean set_custom_property = FALSE;
1785   gboolean parse_custom_node = FALSE;
1786   GList *l, *unresolved, *properties;
1787   ClutterLayoutManager *manager;
1788   GType meta_type;
1789
1790   manager = g_object_get_data (G_OBJECT (container), "clutter-layout-manager");
1791   if (manager == NULL)
1792     return;
1793
1794   meta_type = _clutter_layout_manager_get_child_meta_type (manager);
1795   if (meta_type == G_TYPE_INVALID)
1796     return;
1797
1798   CLUTTER_NOTE (SCRIPT, "Layout manager of type '%s' with meta type '%s'",
1799                 G_OBJECT_TYPE_NAME (manager),
1800                 g_type_name (meta_type));
1801
1802   /* shortcut, to avoid typechecking every time */
1803   if (CLUTTER_IS_SCRIPTABLE (manager))
1804     {
1805       scriptable = CLUTTER_SCRIPTABLE (manager);
1806       iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
1807
1808       parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE;
1809       set_custom_property = iface->set_custom_property != NULL ? TRUE : FALSE;
1810     }
1811
1812   properties = oinfo->properties;
1813   oinfo->properties = NULL;
1814
1815   unresolved = NULL;
1816   for (l = properties; l != NULL; l = l->next)
1817     {
1818       PropertyInfo *pinfo = l->data;
1819       GValue value = G_VALUE_INIT;
1820       gboolean res = FALSE;
1821       const gchar *name;
1822
1823       if (!pinfo->is_layout)
1824         {
1825           unresolved = g_list_prepend (unresolved, pinfo);
1826           continue;
1827         }
1828
1829       name = pinfo->name + strlen ("layout::");
1830
1831       pinfo->pspec =
1832         clutter_layout_manager_find_child_property (manager, name);
1833
1834       if (pinfo->pspec != NULL)
1835         g_param_spec_ref (pinfo->pspec);
1836
1837       CLUTTER_NOTE (SCRIPT, "Parsing %s layout property (id:%s)",
1838                     pinfo->pspec != NULL ? "regular" : "custom",
1839                     name);
1840
1841       if (parse_custom_node)
1842         res = iface->parse_custom_node (scriptable, script, &value,
1843                                         name,
1844                                         pinfo->node);
1845
1846       if (!res)
1847         res = _clutter_script_parse_node (script, &value,
1848                                           name,
1849                                           pinfo->node,
1850                                           pinfo->pspec);
1851
1852       if (!res)
1853         {
1854           CLUTTER_NOTE (SCRIPT, "Layout property '%s' ignored", name);
1855           unresolved = g_list_prepend (unresolved, pinfo);
1856           continue;
1857         }
1858
1859       CLUTTER_NOTE (SCRIPT,
1860                     "Setting %s layout property '%s' (type:%s) to "
1861                     "object '%s' (id:%s)",
1862                     set_custom_property ? "custom" : "regular",
1863                     name,
1864                     g_type_name (G_VALUE_TYPE (&value)),
1865                     g_type_name (oinfo->gtype),
1866                     oinfo->id);
1867
1868       clutter_layout_manager_child_set_property (manager, container, actor,
1869                                                  name,
1870                                                  &value);
1871
1872       g_value_unset (&value);
1873
1874       property_info_free (pinfo);
1875     }
1876
1877   g_list_free (properties);
1878
1879   oinfo->properties = unresolved;
1880 }
1881
1882 static void
1883 apply_child_properties (ClutterScript    *script,
1884                         ClutterContainer *container,
1885                         ClutterActor     *actor,
1886                         ObjectInfo       *oinfo)
1887 {
1888   ClutterScriptable *scriptable = NULL;
1889   ClutterScriptableIface *iface = NULL;
1890   gboolean set_custom_property = FALSE;
1891   gboolean parse_custom_node = FALSE;
1892   GList *l, *unresolved, *properties;
1893   GObjectClass *klass;
1894   GType meta_type;
1895
1896   meta_type = CLUTTER_CONTAINER_GET_IFACE (container)->child_meta_type;
1897   if (meta_type == G_TYPE_INVALID)
1898     return;
1899
1900   klass = G_OBJECT_GET_CLASS (container);
1901
1902   /* shortcut, to avoid typechecking every time */
1903   if (CLUTTER_IS_SCRIPTABLE (container))
1904     {
1905       scriptable = CLUTTER_SCRIPTABLE (container);
1906       iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
1907
1908       parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE;
1909       set_custom_property = iface->set_custom_property != NULL ? TRUE : FALSE;
1910     }
1911
1912   properties = oinfo->properties;
1913   oinfo->properties = NULL;
1914
1915   unresolved = NULL;
1916   for (l = properties; l != NULL; l = l->next)
1917     {
1918       PropertyInfo *pinfo = l->data;
1919       GValue value = G_VALUE_INIT;
1920       gboolean res = FALSE;
1921       const gchar *name;
1922
1923       if (!pinfo->is_child)
1924         {
1925           unresolved = g_list_prepend (unresolved, pinfo);
1926           continue;
1927         }
1928
1929       name = pinfo->name + strlen ("child::");
1930
1931       pinfo->pspec =
1932         clutter_container_class_find_child_property (klass, name);
1933
1934       if (pinfo->pspec != NULL)
1935         g_param_spec_ref (pinfo->pspec);
1936
1937       CLUTTER_NOTE (SCRIPT, "Parsing %s child property (id:%s)",
1938                     pinfo->pspec != NULL ? "regular" : "custom",
1939                     name);
1940
1941       if (parse_custom_node)
1942         res = iface->parse_custom_node (scriptable, script, &value,
1943                                         name,
1944                                         pinfo->node);
1945
1946       if (!res)
1947         res = _clutter_script_parse_node (script, &value,
1948                                           name,
1949                                           pinfo->node,
1950                                           pinfo->pspec);
1951
1952       if (!res)
1953         {
1954           CLUTTER_NOTE (SCRIPT, "Child property '%s' ignored", name);
1955           unresolved = g_list_prepend (unresolved, pinfo);
1956           continue;
1957         }
1958
1959       
1960       CLUTTER_NOTE (SCRIPT,
1961                     "Setting %s child property '%s' (type:%s) to "
1962                     "object '%s' (id:%s)",
1963                     set_custom_property ? "custom" : "regular",
1964                     name,
1965                     g_type_name (G_VALUE_TYPE (&value)),
1966                     g_type_name (oinfo->gtype),
1967                     oinfo->id);
1968
1969       clutter_container_child_set_property (container, actor,
1970                                             name,
1971                                             &value);
1972
1973       g_value_unset (&value);
1974
1975       property_info_free (pinfo);
1976     }
1977
1978   g_list_free (properties);
1979
1980   oinfo->properties = unresolved;
1981 }
1982
1983 static void
1984 add_children (ClutterScript *script,
1985               ObjectInfo    *oinfo)
1986 {
1987   ClutterContainer *container = CLUTTER_CONTAINER (oinfo->object);
1988   GList *l, *unresolved;
1989
1990   unresolved = NULL;
1991   for (l = oinfo->children; l != NULL; l = l->next)
1992     {
1993       const gchar *name = l->data;
1994       GObject *object = NULL;
1995       ObjectInfo *child_info;
1996
1997       child_info = _clutter_script_get_object_info (script, name);
1998       if (child_info != NULL)
1999         {
2000           _clutter_script_construct_object (script, child_info);
2001           object = child_info->object;
2002         }
2003
2004       if (object == NULL)
2005         {
2006           unresolved = g_list_prepend (unresolved, g_strdup (name));
2007           continue;
2008         }
2009
2010       if (!CLUTTER_IS_ACTOR (object))
2011         {
2012           g_warning ("The object definition '%s' (type: %s) is not "
2013                      "an actor, but it is referenced in the 'children' "
2014                      "member of the container '%s' (type: %s); skipping.",
2015                      child_info->id,
2016                      g_type_name (child_info->gtype),
2017                      oinfo->id,
2018                      g_type_name (oinfo->gtype));
2019           continue;
2020         }
2021
2022       CLUTTER_NOTE (SCRIPT, "Adding children '%s' to actor of type '%s'",
2023                     name,
2024                     g_type_name (G_OBJECT_TYPE (container)));
2025
2026       clutter_container_add_actor (container, CLUTTER_ACTOR (object));
2027     }
2028
2029   g_list_foreach (oinfo->children, (GFunc) g_free, NULL);
2030   g_list_free (oinfo->children);
2031
2032   oinfo->children = unresolved;
2033 }
2034
2035 static inline void
2036 _clutter_script_check_unresolved (ClutterScript *script,
2037                                   ObjectInfo    *oinfo)
2038 {
2039   if (oinfo->children != NULL && CLUTTER_IS_CONTAINER (oinfo->object))
2040     add_children (script, oinfo);
2041
2042   /* this is a bit *eugh*, but it allows us to effectively make sure
2043    * that child and layout properties are parsed and applied to the
2044    * right child
2045    */
2046   if (oinfo->properties != NULL && CLUTTER_IS_ACTOR (oinfo->object))
2047     {
2048       ClutterActor *parent;
2049
2050       parent = clutter_actor_get_parent (CLUTTER_ACTOR (oinfo->object));
2051       if (parent != NULL)
2052         {
2053           ClutterContainer *container = CLUTTER_CONTAINER (parent);
2054           ClutterActor *child;
2055
2056           for (child = clutter_actor_get_first_child (parent);
2057                child != NULL;
2058                child = clutter_actor_get_next_sibling (child))
2059             {
2060               ObjectInfo *child_info;
2061               const gchar *id_;
2062
2063               id_ = clutter_get_script_id (G_OBJECT (child));
2064               if (id_ == NULL || *id_ == '\0')
2065                 continue;
2066
2067               child_info = _clutter_script_get_object_info (script, id_);
2068               if (child_info == NULL)
2069                 continue;
2070
2071               apply_child_properties (script, container,
2072                                       child,
2073                                       child_info);
2074               apply_layout_properties (script, container,
2075                                        child,
2076                                        child_info);
2077             }
2078         }
2079     }
2080
2081   if (oinfo->properties || oinfo->children)
2082     oinfo->has_unresolved = TRUE;
2083   else
2084     oinfo->has_unresolved = FALSE;
2085 }
2086
2087 void
2088 _clutter_script_apply_properties (ClutterScript *script,
2089                                   ObjectInfo    *oinfo)
2090 {
2091   ClutterScriptable *scriptable = NULL;
2092   ClutterScriptableIface *iface = NULL;
2093   gboolean set_custom_property = FALSE;
2094   GObject *object = oinfo->object;
2095   GList *properties;
2096   GArray *params;
2097   guint i;
2098
2099   if (!oinfo->has_unresolved)
2100     return;
2101
2102   /* shortcut, to avoid typechecking every time */
2103   if (CLUTTER_IS_SCRIPTABLE (object))
2104     {
2105       scriptable = CLUTTER_SCRIPTABLE (object);
2106       iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
2107
2108       if (iface->set_custom_property)
2109         set_custom_property = TRUE;
2110     }
2111
2112   /* then we get the rest of the parameters, asking the object itself
2113    * to translate them for us, if we cannot do that
2114    */
2115   properties = oinfo->properties;
2116   oinfo->properties = clutter_script_translate_parameters (script,
2117                                                            object,
2118                                                            oinfo->id,
2119                                                            properties,
2120                                                            &params);
2121
2122   /* consume all the properties we could translate in this pass */
2123   for (i = 0; i < params->len; i++)
2124     {
2125       GParameter *param = &g_array_index (params, GParameter, i);
2126
2127       CLUTTER_NOTE (SCRIPT,
2128                     "Setting %s property '%s' (type:%s) to object '%s' (id:%s)",
2129                     set_custom_property ? "custom" : "regular",
2130                     param->name,
2131                     g_type_name (G_VALUE_TYPE (&param->value)),
2132                     g_type_name (oinfo->gtype),
2133                     oinfo->id);
2134
2135       if (set_custom_property)
2136         iface->set_custom_property (scriptable, script,
2137                                     param->name,
2138                                     &param->value);
2139       else
2140         g_object_set_property (object, param->name, &param->value);
2141
2142       g_free ((gchar *) param->name);
2143       g_value_unset (&param->value);
2144     }
2145
2146   g_array_free (params, TRUE);
2147
2148   _clutter_script_check_unresolved (script, oinfo);
2149 }
2150
2151 void
2152 _clutter_script_construct_object (ClutterScript *script,
2153                                   ObjectInfo    *oinfo)
2154 {
2155   GArray *params = NULL;
2156   guint i;
2157
2158   /* we have completely updated the object */
2159   if (oinfo->object != NULL)
2160     {
2161       if (oinfo->has_unresolved)
2162         _clutter_script_check_unresolved (script, oinfo);
2163
2164       return;
2165     }
2166
2167   if (oinfo->gtype == G_TYPE_INVALID)
2168     {
2169       if (G_UNLIKELY (oinfo->type_func))
2170         oinfo->gtype = _clutter_script_get_type_from_symbol (oinfo->type_func);
2171       else
2172         oinfo->gtype = clutter_script_get_type_from_name (script, oinfo->class_name);
2173
2174       if (G_UNLIKELY (oinfo->gtype == G_TYPE_INVALID))
2175         return;
2176
2177       oinfo->is_actor = g_type_is_a (oinfo->gtype, CLUTTER_TYPE_ACTOR);
2178       if (oinfo->is_actor)
2179         oinfo->is_stage = g_type_is_a (oinfo->gtype, CLUTTER_TYPE_STAGE);
2180     }
2181
2182   if (oinfo->is_stage && oinfo->is_stage_default)
2183     {
2184       ClutterStageManager *manager = clutter_stage_manager_get_default ();
2185       GList *properties = oinfo->properties;
2186       ClutterStage *default_stage;
2187
2188       /* the default stage is a complex beast: we cannot create it using
2189        * g_object_newv() but we need clutter_script_construct_parameters()
2190        * to add the GParamSpec to the PropertyInfo pspec member, so
2191        * that we don't have to implement every complex property (like
2192        * the "color" one) directly inside the ClutterStage class.
2193        */
2194       oinfo->properties =
2195         clutter_script_construct_parameters (script,
2196                                              oinfo->gtype,
2197                                              oinfo->id,
2198                                              properties,
2199                                              &params);
2200
2201       default_stage = clutter_stage_manager_get_default_stage (manager);
2202       oinfo->object = G_OBJECT (default_stage);
2203
2204       for (i = 0; i < params->len; i++)
2205         {
2206           GParameter *param = &g_array_index (params, GParameter, i);
2207
2208           g_free ((gchar *) param->name);
2209           g_value_unset (&param->value);
2210         }
2211
2212       g_array_free (params, TRUE);
2213     }
2214   else
2215     {
2216       GList *properties = oinfo->properties;
2217       GParameter *parameters;
2218
2219       /* every other object: first, we get the construction parameters */
2220       oinfo->properties =
2221         clutter_script_construct_parameters (script,
2222                                              oinfo->gtype,
2223                                              oinfo->id,
2224                                              properties,
2225                                              &params);
2226
2227       parameters = (GParameter *) (void *) params->data;
2228       oinfo->object = g_object_newv (oinfo->gtype,
2229                                      params->len,
2230                                      parameters);
2231
2232       /* by sinking the floating reference, we make sure that the reference
2233        * count is correct whether the object is referenced from somewhere
2234        * else too or only by this ClutterScript object.
2235        */
2236       g_object_ref_sink (oinfo->object);
2237
2238       for (i = 0; i < params->len; i++)
2239         {
2240           GParameter *param = &g_array_index (params, GParameter, i);
2241
2242           g_free ((gchar *) param->name);
2243           g_value_unset (&param->value);
2244         }
2245
2246       g_array_free (params, TRUE);
2247    }
2248
2249   g_assert (oinfo->object != NULL);
2250
2251   if (CLUTTER_IS_SCRIPTABLE (oinfo->object))
2252     clutter_scriptable_set_id (CLUTTER_SCRIPTABLE (oinfo->object), oinfo->id);
2253   else
2254     g_object_set_data_full (oinfo->object, "clutter-script-id",
2255                             g_strdup (oinfo->id),
2256                             g_free);
2257
2258   _clutter_script_check_unresolved (script, oinfo);
2259 }