~ubuntu-branches/ubuntu/vivid/clutter-1.0/vivid-proposed

« back to all changes in this revision

Viewing changes to clutter/clutter-script-parser.c

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2010-07-18 17:21:49 UTC
  • mfrom: (1.2.1 upstream) (4.1.3 experimental)
  • Revision ID: james.westby@ubuntu.com-20100718172149-j6s9u4chocaoykme
Tags: 1.2.12-1
* New upstream release.
* debian/libclutter-1.0-0.symbols,
  debian/rules:
  - Add a symbols file.
* debian/rules,
  debian/source/format:
  - Switch to source format 3.0 (quilt).
* debian/control.in:
  - Standards-Version is 3.9.0, no changes needed.
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 *
 
23
 *
 
24
 * Original author:
 
25
 *
 
26
 *      Emmanuele Bassi <ebassi@linux.intel.com>
 
27
 */
 
28
 
1
29
#ifdef HAVE_CONFIG_H
2
30
#include "config.h"
3
31
#endif
11
39
#include "clutter-actor.h"
12
40
#include "clutter-behaviour.h"
13
41
#include "clutter-container.h"
 
42
#include "clutter-debug.h"
 
43
#include "clutter-enum-types.h"
14
44
 
15
45
#include "clutter-script.h"
16
46
#include "clutter-script-private.h"
17
47
#include "clutter-scriptable.h"
18
48
 
19
 
#include "clutter-debug.h"
20
49
#include "clutter-private.h"
21
50
 
 
51
static void clutter_script_parser_object_end (JsonParser *parser,
 
52
                                              JsonObject *object);
 
53
static void clutter_script_parser_parse_end  (JsonParser *parser);
 
54
 
 
55
G_DEFINE_TYPE (ClutterScriptParser, clutter_script_parser, JSON_TYPE_PARSER);
 
56
 
 
57
static void
 
58
clutter_script_parser_class_init (ClutterScriptParserClass *klass)
 
59
{
 
60
  JsonParserClass *parser_class = JSON_PARSER_CLASS (klass);
 
61
 
 
62
  parser_class->object_end = clutter_script_parser_object_end;
 
63
  parser_class->parse_end = clutter_script_parser_parse_end;
 
64
}
 
65
 
 
66
static void
 
67
clutter_script_parser_init (ClutterScriptParser *parser)
 
68
{
 
69
}
 
70
 
22
71
GType
23
72
clutter_script_get_type_from_symbol (const gchar *symbol)
24
73
{
27
76
  GType gtype = G_TYPE_INVALID;
28
77
 
29
78
  if (!module)
30
 
    module = g_module_open (NULL, G_MODULE_BIND_LAZY);
 
79
    module = g_module_open (NULL, 0);
31
80
  
32
81
  if (g_module_symbol (module, symbol, (gpointer)&func))
33
82
    gtype = func ();
46
95
  gint i;
47
96
 
48
97
  if (G_UNLIKELY (!module))
49
 
    module = g_module_open (NULL, G_MODULE_BIND_LAZY);
 
98
    module = g_module_open (NULL, 0);
50
99
  
51
100
  for (i = 0; name[i] != '\0'; i++)
52
101
    {
68
117
       * see:
69
118
       *
70
119
       * http://mail.gnome.org/archives/gtk-devel-list/2007-June/msg00022.html
 
120
       *
 
121
       * and:
 
122
       *
 
123
       * http://git.gnome.org/cgit/gtk+/plain/gtk/gtkbuilderparser.c
71
124
       */
72
125
 
73
126
      if ((c == g_ascii_toupper (c) &&
246
299
parse_knot_from_array (JsonArray   *array,
247
300
                       ClutterKnot *knot)
248
301
{
249
 
  JsonNode *val;
250
 
 
251
 
  if (json_array_get_length (array) < 2)
 
302
  if (json_array_get_length (array) != 2)
252
303
    return FALSE;
253
304
 
254
 
  val = json_array_get_element (array, 0);
255
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
256
 
    knot->x = json_node_get_int (val);
257
 
 
258
 
  val = json_array_get_element (array, 1);
259
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
260
 
    knot->y = json_node_get_int (val);
 
305
  knot->x = json_array_get_int_element (array, 0);
 
306
  knot->y = json_array_get_int_element (array, 1);
261
307
 
262
308
  return TRUE;
263
309
}
266
312
parse_knot_from_object (JsonObject  *object,
267
313
                        ClutterKnot *knot)
268
314
{
269
 
  JsonNode *val;
270
 
 
271
 
  if (json_object_get_size (object) < 2)
272
 
    return FALSE;
273
 
 
274
 
  val = json_object_get_member (object, "x");
275
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
276
 
    knot->x = json_node_get_int (val);
277
 
 
278
 
  val = json_object_get_member (object, "y");
279
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
280
 
    knot->y = json_node_get_int (val);
 
315
  if (json_object_has_member (object, "x"))
 
316
    knot->x = json_object_get_int_member (object, "x");
 
317
  else
 
318
    knot->x = 0;
 
319
 
 
320
  if (json_object_has_member (object, "y"))
 
321
    knot->y = json_object_get_int_member (object, "y");
 
322
  else
 
323
    knot->y = 0;
281
324
 
282
325
  return TRUE;
283
326
}
310
353
parse_geometry_from_array (JsonArray       *array,
311
354
                           ClutterGeometry *geometry)
312
355
{
313
 
  JsonNode *val;
314
 
 
315
 
  if (json_array_get_length (array) < 4)
 
356
  if (json_array_get_length (array) != 4)
316
357
    return FALSE;
317
358
 
318
 
  val = json_array_get_element (array, 0);
319
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
320
 
    geometry->x = json_node_get_int (val);
321
 
 
322
 
  val = json_array_get_element (array, 1);
323
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
324
 
    geometry->y = json_node_get_int (val);
325
 
 
326
 
  val = json_array_get_element (array, 2);
327
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
328
 
    geometry->width = json_node_get_int (val);
329
 
 
330
 
  val = json_array_get_element (array, 3);
331
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
332
 
    geometry->height = json_node_get_int (val);
 
359
  geometry->x = json_array_get_int_element (array, 0);
 
360
  geometry->y = json_array_get_int_element (array, 1);
 
361
  geometry->width = json_array_get_int_element (array, 2);
 
362
  geometry->height = json_array_get_int_element (array, 3);
333
363
 
334
364
  return TRUE;
335
365
}
338
368
parse_geometry_from_object (JsonObject      *object,
339
369
                            ClutterGeometry *geometry)
340
370
{
341
 
  JsonNode *val;
342
 
 
343
 
  if (json_object_get_size (object) < 4)
344
 
    return FALSE;
345
 
 
346
 
  val = json_object_get_member (object, "x");
347
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
348
 
    geometry->x = json_node_get_int (val);
349
 
 
350
 
  val = json_object_get_member (object, "y");
351
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
352
 
    geometry->y = json_node_get_int (val);
353
 
 
354
 
  val = json_object_get_member (object, "width");
355
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
356
 
    geometry->width = json_node_get_int (val);
357
 
 
358
 
  val = json_object_get_member (object, "height");
359
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
360
 
    geometry->height = json_node_get_int (val);
 
371
  if (json_object_has_member (object, "x"))
 
372
    geometry->x = json_object_get_int_member (object, "x");
 
373
  else
 
374
    geometry->x = 0;
 
375
 
 
376
  if (json_object_has_member (object, "y"))
 
377
    geometry->y = json_object_get_int_member (object, "y");
 
378
  else
 
379
    geometry->y = 0;
 
380
 
 
381
  if (json_object_has_member (object, "width"))
 
382
    geometry->width = json_object_get_int_member (object, "width");
 
383
  else
 
384
    geometry->width = 0;
 
385
 
 
386
  if (json_object_has_member (object, "height"))
 
387
    geometry->height = json_object_get_int_member (object, "height");
 
388
  else
 
389
    geometry->height = 0;
361
390
 
362
391
  return TRUE;
363
392
}
390
419
parse_color_from_array (JsonArray    *array,
391
420
                        ClutterColor *color)
392
421
{
393
 
  JsonNode *val;
394
 
 
395
 
  if (json_array_get_length (array) < 4)
 
422
  if (json_array_get_length (array) != 3 ||
 
423
      json_array_get_length (array) != 4)
396
424
    return FALSE;
397
425
 
398
 
  val = json_array_get_element (array, 0);
399
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
400
 
    color->red = CLAMP (json_node_get_int (val), 0, 255);
401
 
 
402
 
  val = json_array_get_element (array, 1);
403
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
404
 
    color->green = CLAMP (json_node_get_int (val), 0, 255);
405
 
 
406
 
  val = json_array_get_element (array, 2);
407
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
408
 
    color->blue = CLAMP (json_node_get_int (val), 0, 255);
409
 
 
410
 
  val = json_array_get_element (array, 3);
411
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
412
 
    color->alpha = CLAMP (json_node_get_int (val), 0, 255);
 
426
  color->red   = CLAMP (json_array_get_int_element (array, 0), 0, 255);
 
427
  color->green = CLAMP (json_array_get_int_element (array, 1), 0, 255);
 
428
  color->blue  = CLAMP (json_array_get_int_element (array, 2), 0, 255);
 
429
 
 
430
  if (json_array_get_length (array) == 4)
 
431
    color->alpha = CLAMP (json_array_get_int_element (array, 3), 0, 255);
 
432
  else
 
433
    color->alpha = 255;
413
434
 
414
435
  return TRUE;
415
436
}
418
439
parse_color_from_object (JsonObject   *object,
419
440
                         ClutterColor *color)
420
441
{
421
 
  JsonNode *val;
422
 
 
423
 
  if (json_object_get_size (object) < 4)
424
 
    return FALSE;
425
 
 
426
 
  val = json_object_get_member (object, "red");
427
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
428
 
    color->red = CLAMP (json_node_get_int (val), 0, 255);
429
 
 
430
 
  val = json_object_get_member (object, "green");
431
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
432
 
    color->green = CLAMP (json_node_get_int (val), 0, 255);
433
 
 
434
 
  val = json_object_get_member (object, "blue");
435
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
436
 
    color->blue = CLAMP (json_node_get_int (val), 0, 255);
437
 
 
438
 
  val = json_object_get_member (object, "alpha");
439
 
  if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE)
440
 
    color->alpha = CLAMP (json_node_get_int (val), 0, 255);
 
442
  if (json_object_has_member (object, "red"))
 
443
    color->red = CLAMP (json_object_get_int_member (object, "red"), 0, 255);
 
444
  else
 
445
    color->red = 0;
 
446
 
 
447
  if (json_object_has_member (object, "green"))
 
448
    color->green = CLAMP (json_object_get_int_member (object, "green"), 0, 255);
 
449
  else
 
450
    color->green = 0;
 
451
 
 
452
  if (json_object_has_member (object, "blue"))
 
453
    color->blue = CLAMP (json_object_get_int_member (object, "blue"), 0, 255);
 
454
  else
 
455
    color->blue = 0;
 
456
 
 
457
  if (json_object_has_member (object, "alpha"))
 
458
    color->alpha = CLAMP (json_object_get_int_member (object, "alpha"), 0, 255);
 
459
  else
 
460
    color->alpha = 255;
441
461
 
442
462
  return TRUE;
443
463
}
459
479
    case JSON_NODE_OBJECT:
460
480
      return parse_color_from_object (json_node_get_object (node), color);
461
481
 
 
482
    case JSON_NODE_VALUE:
 
483
      return clutter_color_from_string (color, json_node_get_string (node));
 
484
 
462
485
    default:
463
486
      break;
464
487
    }
465
488
 
466
489
  return FALSE;
467
490
}
 
491
 
 
492
static const gchar *
 
493
get_id_from_node (JsonNode *node)
 
494
{
 
495
  JsonObject *object;
 
496
 
 
497
  switch (JSON_NODE_TYPE (node))
 
498
    {
 
499
    case JSON_NODE_OBJECT:
 
500
      object = json_node_get_object (node);
 
501
      if (json_object_has_member (object, "id"))
 
502
        return json_object_get_string_member (object, "id");
 
503
      break;
 
504
 
 
505
    case JSON_NODE_VALUE:
 
506
      return json_node_get_string (node);
 
507
 
 
508
    default:
 
509
      break;
 
510
    }
 
511
 
 
512
  return NULL;
 
513
}
 
514
 
 
515
static GList *
 
516
parse_children (ObjectInfo *oinfo,
 
517
                JsonNode   *node)
 
518
{
 
519
  JsonArray *array;
 
520
  GList *retval;
 
521
  guint array_len, i;
 
522
 
 
523
  if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
 
524
    return NULL;
 
525
 
 
526
  retval = oinfo->children;
 
527
 
 
528
  array = json_node_get_array (node);
 
529
  array_len = json_array_get_length (array);
 
530
 
 
531
  for (i = 0; i < array_len; i++)
 
532
    {
 
533
      JsonNode *child = json_array_get_element (array, i);
 
534
      const gchar *id;
 
535
 
 
536
      id = get_id_from_node (child);
 
537
      if (id)
 
538
        retval = g_list_prepend (retval, g_strdup (id));
 
539
    }
 
540
 
 
541
  return g_list_reverse (retval);
 
542
}
 
543
 
 
544
static GList *
 
545
parse_signals (ClutterScript *script,
 
546
               ObjectInfo    *oinfo,
 
547
               JsonNode      *node)
 
548
{
 
549
  JsonArray *array;
 
550
  GList *retval;
 
551
  guint array_len, i;
 
552
 
 
553
  if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
 
554
    {
 
555
      _clutter_script_warn_invalid_value (script, "signals", "Array", node);
 
556
      return NULL;
 
557
    }
 
558
 
 
559
  retval = oinfo->signals;
 
560
  array = json_node_get_array (node);
 
561
  array_len = json_array_get_length (array);
 
562
 
 
563
  for (i = 0; i < array_len; i++)
 
564
    {
 
565
      JsonNode *val = json_array_get_element (array, i);
 
566
      JsonObject *object;
 
567
      SignalInfo *sinfo;
 
568
      const gchar *name;
 
569
      const gchar *handler;
 
570
      const gchar *connect;
 
571
      GConnectFlags flags = 0;
 
572
 
 
573
      if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT)
 
574
        {
 
575
          _clutter_script_warn_invalid_value (script,
 
576
                                              "signals array", "Object",
 
577
                                              node);
 
578
          continue;
 
579
        }
 
580
 
 
581
      object = json_node_get_object (val);
 
582
 
 
583
      /* mandatory: "name" */
 
584
      if (!json_object_has_member (object, "name"))
 
585
        {
 
586
          _clutter_script_warn_missing_attribute (script, NULL, "name");
 
587
          continue;
 
588
        }
 
589
      else
 
590
        {
 
591
          name = json_object_get_string_member (object, "name");
 
592
          if (!name)
 
593
            {
 
594
              _clutter_script_warn_invalid_value (script,
 
595
                                                  "name", "string",
 
596
                                                  val);
 
597
              continue;
 
598
            }
 
599
        }
 
600
 
 
601
      /* mandatory: "handler" */
 
602
      if (!json_object_has_member (object, "handler"))
 
603
        {
 
604
          _clutter_script_warn_missing_attribute (script, NULL, "handler");
 
605
          continue;
 
606
        }
 
607
      else
 
608
        {
 
609
          handler = json_object_get_string_member (object, "handler");
 
610
          if (!handler)
 
611
            {
 
612
              _clutter_script_warn_invalid_value (script,
 
613
                                                  "handler", "string",
 
614
                                                  val);
 
615
              continue;
 
616
            }
 
617
        }
 
618
 
 
619
      /* optional: "object" */
 
620
      if (json_object_has_member (object, "object"))
 
621
        connect = json_object_get_string_member (object, "object");
 
622
      else
 
623
        connect = NULL;
 
624
 
 
625
      /* optional: "after" */
 
626
      if (json_object_has_member (object, "after"))
 
627
        {
 
628
          if (json_object_get_boolean_member (object, "after"))
 
629
            flags |= G_CONNECT_AFTER;
 
630
        }
 
631
 
 
632
      /* optional: "swapped" */
 
633
      if (json_object_has_member (object, "swapped"))
 
634
        {
 
635
          if (json_object_get_boolean_member (object, "swapped"))
 
636
            flags |= G_CONNECT_SWAPPED;
 
637
        }
 
638
 
 
639
      CLUTTER_NOTE (SCRIPT,
 
640
                    "Parsing signal '%s' (handler:%s, object:%s, flags:%d)",
 
641
                    name,
 
642
                    handler, connect, flags);
 
643
 
 
644
      sinfo = g_slice_new0 (SignalInfo);
 
645
      sinfo->name = g_strdup (name);
 
646
      sinfo->handler = g_strdup (handler);
 
647
      sinfo->object = g_strdup (connect);
 
648
      sinfo->flags = flags;
 
649
 
 
650
      retval = g_list_prepend (retval, sinfo);
 
651
    }
 
652
 
 
653
  return retval;
 
654
}
 
655
 
 
656
static GList *
 
657
parse_behaviours (ObjectInfo *oinfo,
 
658
                  JsonNode   *node)
 
659
{
 
660
  JsonArray *array;
 
661
  GList *retval;
 
662
  guint array_len, i;
 
663
 
 
664
  if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
 
665
    return NULL;
 
666
 
 
667
  retval = oinfo->behaviours;
 
668
 
 
669
  array = json_node_get_array (node);
 
670
  array_len = json_array_get_length (array);
 
671
 
 
672
  for (i = 0; i < array_len; i++)
 
673
    {
 
674
      JsonNode *child = json_array_get_element (array, i);
 
675
      const gchar *id;
 
676
 
 
677
      id = get_id_from_node (child);
 
678
      if (id)
 
679
        retval = g_list_prepend (retval, g_strdup (id));
 
680
    }
 
681
 
 
682
  return g_list_reverse (retval);
 
683
}
 
684
 
 
685
static ClutterTimeline *
 
686
construct_timeline (ClutterScript *script,
 
687
                    JsonObject    *object)
 
688
{
 
689
  ClutterTimeline *retval = NULL;
 
690
  ObjectInfo *oinfo;
 
691
  GList *members, *l;
 
692
 
 
693
  /* we fake an ObjectInfo so we can reuse clutter_script_construct_object()
 
694
   * here; we do not save it inside the hash table, because if this had
 
695
   * been a named object then we wouldn't have ended up here in the first
 
696
   * place
 
697
   */
 
698
  oinfo = g_slice_new0 (ObjectInfo);
 
699
  oinfo->gtype = CLUTTER_TYPE_TIMELINE;
 
700
  oinfo->id = g_strdup ("dummy");
 
701
 
 
702
  members = json_object_get_members (object);
 
703
  for (l = members; l != NULL; l = l->next)
 
704
    {
 
705
      const gchar *name = l->data;
 
706
      JsonNode *node = json_object_get_member (object, name);
 
707
      PropertyInfo *pinfo = g_slice_new0 (PropertyInfo);
 
708
 
 
709
      pinfo->name = g_strdelimit (g_strdup (name), G_STR_DELIMITERS, '-');
 
710
      pinfo->node = json_node_copy (node);
 
711
 
 
712
      oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
 
713
    }
 
714
 
 
715
  g_list_free (members);
 
716
 
 
717
  _clutter_script_construct_object (script, oinfo);
 
718
  _clutter_script_apply_properties (script, oinfo);
 
719
  retval = CLUTTER_TIMELINE (oinfo->object);
 
720
 
 
721
  /* we transfer ownership to the alpha function later */
 
722
  oinfo->is_toplevel = FALSE;
 
723
  object_info_free (oinfo);
 
724
 
 
725
  return retval;
 
726
}
 
727
 
 
728
/* define the names of the animation modes to match the ones
 
729
 * that developers might be more accustomed to
 
730
 */
 
731
static const struct
 
732
{
 
733
  const gchar *name;
 
734
  ClutterAnimationMode mode;
 
735
} animation_modes[] = {
 
736
  { "linear", CLUTTER_LINEAR },
 
737
  { "easeInQuad", CLUTTER_EASE_IN_QUAD },
 
738
  { "easeOutQuad", CLUTTER_EASE_OUT_QUAD },
 
739
  { "easeInOutQuad", CLUTTER_EASE_IN_OUT_QUAD },
 
740
  { "easeInCubic", CLUTTER_EASE_IN_CUBIC },
 
741
  { "easeOutCubic", CLUTTER_EASE_OUT_CUBIC },
 
742
  { "easeInOutCubic", CLUTTER_EASE_IN_OUT_CUBIC },
 
743
  { "easeInQuart", CLUTTER_EASE_IN_QUART },
 
744
  { "easeOutQuart", CLUTTER_EASE_OUT_QUART },
 
745
  { "easeInOutQuart", CLUTTER_EASE_IN_OUT_QUART },
 
746
  { "easeInQuint", CLUTTER_EASE_IN_QUINT },
 
747
  { "easeOutQuint", CLUTTER_EASE_OUT_QUINT },
 
748
  { "easeInOutQuint", CLUTTER_EASE_IN_OUT_QUINT },
 
749
  { "easeInSine", CLUTTER_EASE_IN_SINE },
 
750
  { "easeOutSine", CLUTTER_EASE_OUT_SINE },
 
751
  { "easeInOutSine", CLUTTER_EASE_IN_OUT_SINE },
 
752
  { "easeInExpo", CLUTTER_EASE_IN_EXPO },
 
753
  { "easeOutExpo", CLUTTER_EASE_OUT_EXPO },
 
754
  { "easeInOutExpo", CLUTTER_EASE_IN_OUT_EXPO },
 
755
  { "easeInCirc", CLUTTER_EASE_IN_CIRC },
 
756
  { "easeOutCirc", CLUTTER_EASE_OUT_CIRC },
 
757
  { "easeInOutCirc", CLUTTER_EASE_IN_OUT_CIRC },
 
758
  { "easeInElastic", CLUTTER_EASE_IN_ELASTIC },
 
759
  { "easeOutElastic", CLUTTER_EASE_OUT_ELASTIC },
 
760
  { "easeInOutElastic", CLUTTER_EASE_IN_OUT_ELASTIC },
 
761
  { "easeInBack", CLUTTER_EASE_IN_BACK },
 
762
  { "easeOutBack", CLUTTER_EASE_OUT_BACK },
 
763
  { "easeInOutBack", CLUTTER_EASE_IN_OUT_BACK },
 
764
  { "easeInBounce", CLUTTER_EASE_IN_BOUNCE },
 
765
  { "easeOutBounce", CLUTTER_EASE_OUT_BOUNCE },
 
766
  { "easeInOutBounce", CLUTTER_EASE_IN_OUT_BOUNCE },
 
767
};
 
768
 
 
769
static const gint n_animation_modes = G_N_ELEMENTS (animation_modes);
 
770
 
 
771
gulong
 
772
clutter_script_resolve_animation_mode (JsonNode *node)
 
773
{
 
774
  gint i, res = CLUTTER_CUSTOM_MODE;
 
775
 
 
776
  if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
 
777
    return CLUTTER_CUSTOM_MODE;
 
778
 
 
779
  if (json_node_get_value_type (node) == G_TYPE_INT64)
 
780
    return json_node_get_int (node);
 
781
 
 
782
  if (json_node_get_value_type (node) == G_TYPE_STRING)
 
783
    {
 
784
      const gchar *name = json_node_get_string (node);
 
785
 
 
786
      /* XXX - we might be able to optimize by changing the ordering
 
787
       * of the animation_modes array, e.g.
 
788
       *  - special casing linear
 
789
       *  - tokenizing ('ease', 'In', 'Sine') and matching on token
 
790
       *  - binary searching?
 
791
       */
 
792
      for (i = 0; i < n_animation_modes; i++)
 
793
        {
 
794
          if (strcmp (animation_modes[i].name, name) == 0)
 
795
            return animation_modes[i].mode;
 
796
        }
 
797
 
 
798
      if (clutter_script_enum_from_string (CLUTTER_TYPE_ANIMATION_MODE,
 
799
                                           name,
 
800
                                           &res))
 
801
        return res;
 
802
 
 
803
      g_warning ("Unable to find the animation mode '%s'", name);
 
804
    }
 
805
 
 
806
  return CLUTTER_CUSTOM_MODE;
 
807
}
 
808
 
 
809
static ClutterAlphaFunc
 
810
resolve_alpha_func (const gchar *name)
 
811
{
 
812
  static GModule *module = NULL;
 
813
  ClutterAlphaFunc func;
 
814
 
 
815
  CLUTTER_NOTE (SCRIPT, "Looking up '%s' alpha function", name);
 
816
 
 
817
  if (G_UNLIKELY (!module))
 
818
    module = g_module_open (NULL, 0);
 
819
 
 
820
  if (g_module_symbol (module, name, (gpointer) &func))
 
821
    {
 
822
      CLUTTER_NOTE (SCRIPT, "Found '%s' alpha function in the symbols table",
 
823
                    name);
 
824
      return func;
 
825
    }
 
826
 
 
827
  return NULL;
 
828
}
 
829
 
 
830
GObject *
 
831
_clutter_script_parse_alpha (ClutterScript *script,
 
832
                             JsonNode      *node)
 
833
{
 
834
  GObject *retval = NULL;
 
835
  JsonObject *object;
 
836
  ClutterTimeline *timeline = NULL;
 
837
  ClutterAlphaFunc alpha_func = NULL;
 
838
  ClutterAnimationMode mode = CLUTTER_CUSTOM_MODE;
 
839
  JsonNode *val;
 
840
  gboolean unref_timeline = FALSE;
 
841
 
 
842
  if (JSON_NODE_TYPE (node) != JSON_NODE_OBJECT)
 
843
    return NULL;
 
844
 
 
845
  object = json_node_get_object (node);
 
846
 
 
847
  val = json_object_get_member (object, "timeline");
 
848
  if (val)
 
849
    {
 
850
      if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE &&
 
851
          json_node_get_string (val) != NULL)
 
852
        {
 
853
          const gchar *id = json_node_get_string (val);
 
854
 
 
855
          timeline =
 
856
            CLUTTER_TIMELINE (clutter_script_get_object (script, id));
 
857
        }
 
858
      else if (JSON_NODE_TYPE (val) == JSON_NODE_OBJECT)
 
859
        {
 
860
          timeline = construct_timeline (script, json_node_get_object (val));
 
861
          unref_timeline = TRUE;
 
862
        }
 
863
    }
 
864
 
 
865
  val = json_object_get_member (object, "mode");
 
866
  if (val)
 
867
    mode = clutter_script_resolve_animation_mode (val);
 
868
 
 
869
  if (mode == CLUTTER_CUSTOM_MODE)
 
870
    {
 
871
      val = json_object_get_member (object, "function");
 
872
      if (val && json_node_get_string (val) != NULL)
 
873
        {
 
874
          alpha_func = resolve_alpha_func (json_node_get_string (val));
 
875
          if (!alpha_func)
 
876
            {
 
877
              g_warning ("Unable to find the function '%s' in the "
 
878
                         "Clutter alpha functions or the symbols table",
 
879
                         json_node_get_string (val));
 
880
            }
 
881
        }
 
882
    }
 
883
 
 
884
  CLUTTER_NOTE (SCRIPT, "Parsed alpha: %s timeline (%p) (mode:%d, func:%p)",
 
885
                unref_timeline ? "implicit" : "explicit",
 
886
                timeline ? timeline : 0x0,
 
887
                mode != CLUTTER_CUSTOM_MODE ? mode : 0,
 
888
                alpha_func ? alpha_func : 0x0);
 
889
 
 
890
  retval = g_object_new (CLUTTER_TYPE_ALPHA, NULL);
 
891
 
 
892
  if (mode != CLUTTER_CUSTOM_MODE)
 
893
    clutter_alpha_set_mode (CLUTTER_ALPHA (retval), mode);
 
894
 
 
895
  if (alpha_func != NULL)
 
896
    clutter_alpha_set_func (CLUTTER_ALPHA (retval), alpha_func, NULL, NULL);
 
897
 
 
898
  clutter_alpha_set_timeline (CLUTTER_ALPHA (retval), timeline);
 
899
  if (unref_timeline)
 
900
    g_object_unref (timeline);
 
901
 
 
902
  return retval;
 
903
}
 
904
 
 
905
static void
 
906
clutter_script_parser_object_end (JsonParser *json_parser,
 
907
                                  JsonObject *object)
 
908
{
 
909
  ClutterScriptParser *parser = CLUTTER_SCRIPT_PARSER (json_parser);
 
910
  ClutterScript *script = parser->script;
 
911
  ObjectInfo *oinfo;
 
912
  JsonNode *val;
 
913
  const gchar *id;
 
914
  GList *members, *l;
 
915
 
 
916
  if (!json_object_has_member (object, "id"))
 
917
    {
 
918
      gchar *fake;
 
919
 
 
920
      if (!json_object_has_member (object, "type"))
 
921
        return;
 
922
 
 
923
      fake = _clutter_script_generate_fake_id (script);
 
924
 
 
925
      val = json_node_new (JSON_NODE_VALUE);
 
926
      json_node_set_string (val, fake);
 
927
      json_object_set_member (object, "id", val);
 
928
 
 
929
      g_free (fake);
 
930
    }
 
931
 
 
932
  if (!json_object_has_member (object, "type"))
 
933
    {
 
934
      val = json_object_get_member (object, "id");
 
935
 
 
936
      _clutter_script_warn_missing_attribute (script,
 
937
                                              json_node_get_string (val),
 
938
                                              "type");
 
939
      return;
 
940
    }
 
941
 
 
942
  id = json_object_get_string_member (object, "id");
 
943
 
 
944
  oinfo = _clutter_script_get_object_info (script, id);
 
945
  if (G_LIKELY (!oinfo))
 
946
    {
 
947
      const gchar *class_name;
 
948
 
 
949
      oinfo = g_slice_new0 (ObjectInfo);
 
950
      oinfo->merge_id = _clutter_script_get_last_merge_id (script);
 
951
      oinfo->id = g_strdup (id);
 
952
 
 
953
      class_name = json_object_get_string_member (object, "type");
 
954
      oinfo->class_name = g_strdup (class_name);
 
955
 
 
956
      if (json_object_has_member (object, "type_func"))
 
957
        {
 
958
          const gchar *type_func;
 
959
 
 
960
          type_func = json_object_get_string_member (object, "type_func");
 
961
          oinfo->type_func = g_strdup (type_func);
 
962
 
 
963
          json_object_remove_member (object, "type_func");
 
964
        }
 
965
    }
 
966
 
 
967
  if (json_object_has_member (object, "children"))
 
968
    {
 
969
      val = json_object_get_member (object, "children");
 
970
      oinfo->children = parse_children (oinfo, val);
 
971
 
 
972
      json_object_remove_member (object, "children");
 
973
    }
 
974
 
 
975
  if (json_object_has_member (object, "behaviours"))
 
976
    {
 
977
      val = json_object_get_member (object, "behaviours");
 
978
      oinfo->behaviours = parse_behaviours (oinfo, val);
 
979
 
 
980
      json_object_remove_member (object, "behaviours");
 
981
    }
 
982
 
 
983
  if (json_object_has_member (object, "signals"))
 
984
    {
 
985
      val = json_object_get_member (object, "signals");
 
986
      oinfo->signals = parse_signals (script, oinfo, val);
 
987
 
 
988
      json_object_remove_member (object, "signals");
 
989
    }
 
990
 
 
991
  if (strcmp (oinfo->class_name, "ClutterStage") == 0 &&
 
992
      json_object_has_member (object, "is-default"))
 
993
    {
 
994
      oinfo->is_stage_default =
 
995
        json_object_get_boolean_member (object, "is-default");
 
996
 
 
997
      json_object_remove_member (object, "is-default");
 
998
    }
 
999
  else
 
1000
    oinfo->is_stage_default = FALSE;
 
1001
 
 
1002
  oinfo->is_toplevel = FALSE;
 
1003
  oinfo->is_unmerged = FALSE;
 
1004
  oinfo->has_unresolved = TRUE;
 
1005
 
 
1006
  members = json_object_get_members (object);
 
1007
  for (l = members; l; l = l->next)
 
1008
    {
 
1009
      const gchar *name = l->data;
 
1010
      PropertyInfo *pinfo;
 
1011
      JsonNode *node;
 
1012
 
 
1013
      /* we have already parsed these */
 
1014
      if (strcmp (name, "id") == 0 || strcmp (name, "type") == 0)
 
1015
        continue;
 
1016
 
 
1017
      node = json_object_get_member (object, name);
 
1018
 
 
1019
      pinfo = g_slice_new (PropertyInfo);
 
1020
 
 
1021
      pinfo->name = g_strdup (name);
 
1022
      pinfo->node = json_node_copy (node);
 
1023
      pinfo->pspec = NULL;
 
1024
      pinfo->is_child = g_str_has_prefix (name, "child::") ? TRUE : FALSE;
 
1025
 
 
1026
      oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
 
1027
    }
 
1028
 
 
1029
  g_list_free (members);
 
1030
 
 
1031
  CLUTTER_NOTE (SCRIPT,
 
1032
                "Added object '%s' (type:%s, id:%d, props:%d, signals:%d)",
 
1033
                oinfo->id,
 
1034
                oinfo->class_name,
 
1035
                oinfo->merge_id,
 
1036
                g_list_length (oinfo->properties),
 
1037
                g_list_length (oinfo->signals));
 
1038
 
 
1039
  _clutter_script_add_object_info (script, oinfo);
 
1040
  _clutter_script_construct_object (script, oinfo);
 
1041
}
 
1042
 
 
1043
static void
 
1044
clutter_script_parser_parse_end (JsonParser *parser)
 
1045
{
 
1046
  clutter_script_ensure_objects (CLUTTER_SCRIPT_PARSER (parser)->script);
 
1047
}
 
1048
 
 
1049
gboolean
 
1050
clutter_script_parse_node (ClutterScript *script,
 
1051
                           GValue        *value,
 
1052
                           const gchar   *name,
 
1053
                           JsonNode      *node,
 
1054
                           GParamSpec    *pspec)
 
1055
{
 
1056
  GValue node_value = { 0, };
 
1057
  gboolean retval = FALSE;
 
1058
 
 
1059
  g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
 
1060
  g_return_val_if_fail (name != NULL, FALSE);
 
1061
  g_return_val_if_fail (node != NULL, FALSE);
 
1062
 
 
1063
  switch (JSON_NODE_TYPE (node))
 
1064
    {
 
1065
    case JSON_NODE_OBJECT:
 
1066
      /* if we don't have a GParamSpec we can't infer the type
 
1067
       * of the property; this usually means that this property
 
1068
       * is a custom member that will be parsed by the Scriptable
 
1069
       * interface implementantion
 
1070
       */
 
1071
      if (pspec == NULL)
 
1072
        return FALSE;
 
1073
      else
 
1074
        {
 
1075
          GType p_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
 
1076
          ObjectInfo *oinfo;
 
1077
          const gchar *id;
 
1078
 
 
1079
          g_value_init (value, p_type);
 
1080
 
 
1081
          if (g_type_is_a (p_type, G_TYPE_OBJECT))
 
1082
            {
 
1083
              /* default GObject handling: we get the id and
 
1084
               * retrieve the ObjectInfo for it; since the object
 
1085
               * definitions are parsed leaf-first we are guaranteed
 
1086
               * to have a defined object at this point
 
1087
               */
 
1088
              id = get_id_from_node (node);
 
1089
              if (id == NULL || *id == '\0')
 
1090
                return FALSE;
 
1091
 
 
1092
              oinfo = _clutter_script_get_object_info (script, id);
 
1093
              if (oinfo == NULL || oinfo->gtype == G_TYPE_INVALID )
 
1094
                return FALSE;
 
1095
 
 
1096
              if (g_type_is_a (oinfo->gtype, p_type))
 
1097
                {
 
1098
                  /* force construction, even though it should
 
1099
                   * not be necessary; we don't need the properties
 
1100
                   * to be applied as well: they will when the
 
1101
                   * ScriptParser finishes
 
1102
                   */
 
1103
                  _clutter_script_construct_object (script, oinfo);
 
1104
 
 
1105
                  g_value_set_object (value, oinfo->object);
 
1106
 
 
1107
                  return TRUE;
 
1108
                }
 
1109
            }
 
1110
          else if (p_type == CLUTTER_TYPE_KNOT)
 
1111
            {
 
1112
              ClutterKnot knot = { 0, };
 
1113
 
 
1114
              /* knot := { "x" : (int), "y" : (int) } */
 
1115
 
 
1116
              if (clutter_script_parse_knot (script, node, &knot))
 
1117
                {
 
1118
                  g_value_set_boxed (value, &knot);
 
1119
                  return TRUE;
 
1120
                }
 
1121
            }
 
1122
          else if (p_type == CLUTTER_TYPE_GEOMETRY)
 
1123
            {
 
1124
              ClutterGeometry geom = { 0, };
 
1125
 
 
1126
              /* geometry := {
 
1127
               *        "x" : (int),
 
1128
               *        "y" : (int),
 
1129
               *        "width" : (int),
 
1130
               *        "height" : (int)
 
1131
               * }
 
1132
               */
 
1133
 
 
1134
              if (clutter_script_parse_geometry (script, node, &geom))
 
1135
                {
 
1136
                  g_value_set_boxed (value, &geom);
 
1137
                  return TRUE;
 
1138
                }
 
1139
            }
 
1140
          else if (p_type == CLUTTER_TYPE_COLOR)
 
1141
            {
 
1142
              ClutterColor color = { 0, };
 
1143
 
 
1144
              /* color := {
 
1145
               *        "red" : (int),
 
1146
               *        "green" : (int),
 
1147
               *        "blue" : (int),
 
1148
               *        "alpha" : (int)
 
1149
               * }
 
1150
               */
 
1151
 
 
1152
              if (clutter_script_parse_color (script, node, &color))
 
1153
                {
 
1154
                  g_value_set_boxed (value, &color);
 
1155
                  return TRUE;
 
1156
                }
 
1157
            }
 
1158
         }
 
1159
      return FALSE;
 
1160
 
 
1161
    case JSON_NODE_ARRAY:
 
1162
      if (!pspec)
 
1163
        return FALSE;
 
1164
      else
 
1165
        {
 
1166
          g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
 
1167
 
 
1168
          if (G_VALUE_HOLDS (value, CLUTTER_TYPE_KNOT))
 
1169
            {
 
1170
              ClutterKnot knot = { 0, };
 
1171
 
 
1172
              /* knot := [ (int), (int) ] */
 
1173
 
 
1174
              if (clutter_script_parse_knot (script, node, &knot))
 
1175
                {
 
1176
                  g_value_set_boxed (value, &knot);
 
1177
                  return TRUE;
 
1178
                }
 
1179
            }
 
1180
          else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_GEOMETRY))
 
1181
            {
 
1182
              ClutterGeometry geom = { 0, };
 
1183
 
 
1184
              /* geometry := [ (int), (int), (int), (int) ] */
 
1185
 
 
1186
              if (clutter_script_parse_geometry (script, node, &geom))
 
1187
                {
 
1188
                  g_value_set_boxed (value, &geom);
 
1189
                  return TRUE;
 
1190
                }
 
1191
            }
 
1192
          else if (CLUTTER_VALUE_HOLDS_COLOR (value))
 
1193
            {
 
1194
              ClutterColor color = { 0, };
 
1195
 
 
1196
              /* color := [ (int), (int), (int), (int) ] */
 
1197
 
 
1198
              if (clutter_script_parse_color (script, node, &color))
 
1199
                {
 
1200
                  g_value_set_boxed (value, &color);
 
1201
                  return TRUE;
 
1202
                }
 
1203
            }
 
1204
          else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
 
1205
            {
 
1206
              JsonArray *array = json_node_get_array (node);
 
1207
              guint i, array_len = json_array_get_length (array);
 
1208
              GPtrArray *str_array = g_ptr_array_sized_new (array_len);
 
1209
 
 
1210
              /* strv := [ (str), (str), ... ] */
 
1211
 
 
1212
              for (i = 0; i < array_len; i++)
 
1213
                {
 
1214
                  JsonNode *val = json_array_get_element (array, i);
 
1215
 
 
1216
                  if (JSON_NODE_TYPE (val) != JSON_NODE_VALUE &&
 
1217
                      json_node_get_string (val) == NULL)
 
1218
                    continue;
 
1219
 
 
1220
                  g_ptr_array_add (str_array,
 
1221
                                   (gpointer) json_node_get_string (val));
 
1222
                }
 
1223
 
 
1224
              g_value_set_boxed (value, str_array->pdata);
 
1225
              g_ptr_array_free (str_array, TRUE);
 
1226
 
 
1227
              return TRUE;
 
1228
            }
 
1229
        }
 
1230
      return FALSE;
 
1231
 
 
1232
    case JSON_NODE_NULL:
 
1233
      return FALSE;
 
1234
 
 
1235
    case JSON_NODE_VALUE:
 
1236
      json_node_get_value (node, &node_value);
 
1237
 
 
1238
      if (pspec)
 
1239
        g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
 
1240
      else
 
1241
        g_value_init (value, G_VALUE_TYPE (&node_value));
 
1242
 
 
1243
      switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)))
 
1244
        {
 
1245
        /* fundamental JSON types */
 
1246
        case G_TYPE_INT64:
 
1247
        case G_TYPE_DOUBLE:
 
1248
        case G_TYPE_STRING:
 
1249
        case G_TYPE_BOOLEAN:
 
1250
          g_value_copy (&node_value, value);
 
1251
          retval = TRUE;
 
1252
          break;
 
1253
 
 
1254
        case G_TYPE_INT:
 
1255
          g_value_set_int (value, g_value_get_int64 (&node_value));
 
1256
          retval = TRUE;
 
1257
          break;
 
1258
 
 
1259
        case G_TYPE_UINT:
 
1260
          g_value_set_uint (value, (guint) g_value_get_int64 (&node_value));
 
1261
          retval = TRUE;
 
1262
          break;
 
1263
 
 
1264
        case G_TYPE_ULONG:
 
1265
          g_value_set_ulong (value, (gulong) g_value_get_int64 (&node_value));
 
1266
          retval = TRUE;
 
1267
          break;
 
1268
 
 
1269
        case G_TYPE_UCHAR:
 
1270
          g_value_set_uchar (value, (guchar) g_value_get_int64 (&node_value));
 
1271
          retval = TRUE;
 
1272
          break;
 
1273
 
 
1274
        case G_TYPE_FLOAT:
 
1275
          if (G_VALUE_HOLDS (&node_value, G_TYPE_DOUBLE))
 
1276
            {
 
1277
              g_value_set_float (value, g_value_get_double (&node_value));
 
1278
              retval = TRUE;
 
1279
            }
 
1280
          else if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
 
1281
            {
 
1282
              g_value_set_float (value, g_value_get_int64 (&node_value));
 
1283
              retval = TRUE;
 
1284
            }
 
1285
          break;
 
1286
 
 
1287
        case G_TYPE_ENUM:
 
1288
          if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
 
1289
            {
 
1290
              g_value_set_enum (value, g_value_get_int64 (&node_value));
 
1291
              retval = TRUE;
 
1292
            }
 
1293
          else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
 
1294
            {
 
1295
              gint enum_value;
 
1296
 
 
1297
              retval = clutter_script_enum_from_string (G_VALUE_TYPE (value),
 
1298
                                                        g_value_get_string (&node_value),
 
1299
                                                        &enum_value);
 
1300
              if (retval)
 
1301
                g_value_set_enum (value, enum_value);
 
1302
            }
 
1303
          break;
 
1304
 
 
1305
        case G_TYPE_FLAGS:
 
1306
          if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
 
1307
            {
 
1308
              g_value_set_flags (value, g_value_get_int64 (&node_value));
 
1309
              retval = TRUE;
 
1310
            }
 
1311
          else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
 
1312
            {
 
1313
              gint flags_value;
 
1314
 
 
1315
              retval = clutter_script_flags_from_string (G_VALUE_TYPE (value),
 
1316
                                                         g_value_get_string (&node_value),
 
1317
                                                         &flags_value);
 
1318
              if (retval)
 
1319
                g_value_set_flags (value, flags_value);
 
1320
            }
 
1321
          break;
 
1322
 
 
1323
        case G_TYPE_BOXED:
 
1324
          if (G_VALUE_HOLDS (value, CLUTTER_TYPE_COLOR))
 
1325
            {
 
1326
              ClutterColor color = { 0, };
 
1327
 
 
1328
              retval = clutter_script_parse_color (script, node, &color);
 
1329
              if (retval)
 
1330
                clutter_value_set_color (value, &color);
 
1331
            }
 
1332
          break;
 
1333
 
 
1334
        case G_TYPE_OBJECT:
 
1335
          if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
 
1336
            {
 
1337
              const gchar *str = g_value_get_string (&node_value);
 
1338
              GObject *object = clutter_script_get_object (script, str);
 
1339
              if (object)
 
1340
                {
 
1341
                  CLUTTER_NOTE (SCRIPT,
 
1342
                                "Assigning '%s' (%s) to property '%s'",
 
1343
                                str,
 
1344
                                G_OBJECT_TYPE_NAME (object),
 
1345
                                name);
 
1346
 
 
1347
                  g_value_set_object (value, object);
 
1348
                  retval = TRUE;
 
1349
                }
 
1350
            }
 
1351
          break;
 
1352
 
 
1353
        default:
 
1354
          retval = FALSE;
 
1355
          break;
 
1356
        }
 
1357
 
 
1358
      g_value_unset (&node_value);
 
1359
      break;
 
1360
    }
 
1361
 
 
1362
  return retval;
 
1363
}
 
1364
 
 
1365
static GList *
 
1366
clutter_script_translate_parameters (ClutterScript  *script,
 
1367
                                     GObject        *object,
 
1368
                                     const gchar    *name,
 
1369
                                     GList          *properties,
 
1370
                                     GArray        **params)
 
1371
{
 
1372
  ClutterScriptable *scriptable = NULL;
 
1373
  ClutterScriptableIface *iface = NULL;
 
1374
  GList *l, *unparsed;
 
1375
  gboolean parse_custom = FALSE;
 
1376
 
 
1377
  *params = g_array_new (FALSE, FALSE, sizeof (GParameter));
 
1378
 
 
1379
  if (CLUTTER_IS_SCRIPTABLE (object))
 
1380
    {
 
1381
      scriptable = CLUTTER_SCRIPTABLE (object);
 
1382
      iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
 
1383
 
 
1384
      if (iface->parse_custom_node)
 
1385
        parse_custom = TRUE;
 
1386
    }
 
1387
 
 
1388
  unparsed = NULL;
 
1389
 
 
1390
  for (l = properties; l != NULL; l = l->next)
 
1391
    {
 
1392
      PropertyInfo *pinfo = l->data;
 
1393
      GParameter param = { NULL };
 
1394
      gboolean res = FALSE;
 
1395
 
 
1396
      if (pinfo->is_child)
 
1397
        {
 
1398
          CLUTTER_NOTE (SCRIPT, "Child property '%s' ignored", pinfo->name);
 
1399
          unparsed = g_list_prepend (unparsed, pinfo);
 
1400
          continue;
 
1401
        }
 
1402
 
 
1403
      CLUTTER_NOTE (SCRIPT, "Parsing %s property (id:%s)",
 
1404
                    pinfo->pspec ? "regular" : "custom",
 
1405
                    pinfo->name);
 
1406
 
 
1407
      if (parse_custom)
 
1408
        res = iface->parse_custom_node (scriptable, script, &param.value,
 
1409
                                        pinfo->name,
 
1410
                                        pinfo->node);
 
1411
 
 
1412
      if (!res)
 
1413
        res = clutter_script_parse_node (script, &param.value,
 
1414
                                         pinfo->name,
 
1415
                                         pinfo->node,
 
1416
                                         pinfo->pspec);
 
1417
 
 
1418
      if (!res)
 
1419
        {
 
1420
          CLUTTER_NOTE (SCRIPT, "Property '%s' ignored", pinfo->name);
 
1421
          unparsed = g_list_prepend (unparsed, pinfo);
 
1422
          continue;
 
1423
        }
 
1424
 
 
1425
      param.name = g_strdup (pinfo->name);
 
1426
 
 
1427
      g_array_append_val (*params, param);
 
1428
 
 
1429
      property_info_free (pinfo);
 
1430
    }
 
1431
 
 
1432
  g_list_free (properties);
 
1433
 
 
1434
  return unparsed;
 
1435
}
 
1436
 
 
1437
static GList *
 
1438
clutter_script_construct_parameters (ClutterScript  *script,
 
1439
                                     GType           gtype,
 
1440
                                     const gchar    *name,
 
1441
                                     GList          *properties,
 
1442
                                     GArray        **construct_params)
 
1443
{
 
1444
  GObjectClass *klass;
 
1445
  GList *l, *unparsed;
 
1446
 
 
1447
  klass = g_type_class_ref (gtype);
 
1448
  g_assert (klass != NULL);
 
1449
 
 
1450
  *construct_params = g_array_new (FALSE, FALSE, sizeof (GParameter));
 
1451
 
 
1452
  unparsed = NULL;
 
1453
 
 
1454
  for (l = properties; l != NULL; l = l->next)
 
1455
    {
 
1456
      PropertyInfo *pinfo = l->data;
 
1457
      GParameter param = { NULL };
 
1458
      GParamSpec *pspec = NULL;
 
1459
 
 
1460
      /* we allow custom property names for classes, so if we
 
1461
       * don't find a corresponding GObject property for this
 
1462
       * class we just skip it and let the class itself deal
 
1463
       * with it later on
 
1464
       */
 
1465
      pspec = g_object_class_find_property (klass, pinfo->name);
 
1466
      if (pspec)
 
1467
        pinfo->pspec = g_param_spec_ref (pspec);
 
1468
      else
 
1469
        {
 
1470
          pinfo->pspec = NULL;
 
1471
          unparsed = g_list_prepend (unparsed, pinfo);
 
1472
          continue;
 
1473
        }
 
1474
 
 
1475
      if (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY))
 
1476
        {
 
1477
          unparsed = g_list_prepend (unparsed, pinfo);
 
1478
          continue;
 
1479
        }
 
1480
 
 
1481
      param.name = g_strdup (pinfo->name);
 
1482
 
 
1483
      if (!clutter_script_parse_node (script, &param.value,
 
1484
                                      pinfo->name,
 
1485
                                      pinfo->node,
 
1486
                                      pinfo->pspec))
 
1487
        {
 
1488
          unparsed = g_list_prepend (unparsed, pinfo);
 
1489
          continue;
 
1490
        }
 
1491
 
 
1492
      g_array_append_val (*construct_params, param);
 
1493
 
 
1494
      property_info_free (pinfo);
 
1495
    }
 
1496
 
 
1497
  g_list_free (properties);
 
1498
 
 
1499
  g_type_class_unref (klass);
 
1500
 
 
1501
  return unparsed;
 
1502
}
 
1503
 
 
1504
static void
 
1505
apply_child_properties (ClutterScript    *script,
 
1506
                        ClutterContainer *container,
 
1507
                        ClutterActor     *actor,
 
1508
                        ObjectInfo       *oinfo)
 
1509
{
 
1510
  ClutterScriptable *scriptable = NULL;
 
1511
  ClutterScriptableIface *iface = NULL;
 
1512
  gboolean set_custom_property = FALSE;
 
1513
  gboolean parse_custom_node = FALSE;
 
1514
  GList *l, *unresolved, *properties;
 
1515
  GObjectClass *klass;
 
1516
  GType meta_type;
 
1517
 
 
1518
  meta_type = CLUTTER_CONTAINER_GET_IFACE (container)->child_meta_type;
 
1519
  if (meta_type == G_TYPE_INVALID)
 
1520
    return;
 
1521
 
 
1522
  klass = G_OBJECT_GET_CLASS (container);
 
1523
 
 
1524
  /* shortcut, to avoid typechecking every time */
 
1525
  if (CLUTTER_IS_SCRIPTABLE (container))
 
1526
    {
 
1527
      scriptable = CLUTTER_SCRIPTABLE (container);
 
1528
      iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
 
1529
 
 
1530
      parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE;
 
1531
      set_custom_property = iface->set_custom_property != NULL ? TRUE : FALSE;
 
1532
    }
 
1533
 
 
1534
  properties = oinfo->properties;
 
1535
  oinfo->properties = NULL;
 
1536
 
 
1537
  unresolved = NULL;
 
1538
  for (l = properties; l != NULL; l = l->next)
 
1539
    {
 
1540
      PropertyInfo *pinfo = l->data;
 
1541
      GValue value = { 0, };
 
1542
      gboolean res = FALSE;
 
1543
      const gchar *name;
 
1544
 
 
1545
      if (!pinfo->is_child)
 
1546
        {
 
1547
          unresolved = g_list_prepend (unresolved, pinfo);
 
1548
          continue;
 
1549
        }
 
1550
 
 
1551
      name = pinfo->name + strlen ("child::");
 
1552
 
 
1553
      pinfo->pspec =
 
1554
        clutter_container_class_find_child_property (klass, name);
 
1555
 
 
1556
      if (pinfo->pspec != NULL)
 
1557
        g_param_spec_ref (pinfo->pspec);
 
1558
 
 
1559
      CLUTTER_NOTE (SCRIPT, "Parsing %s child property (id:%s)",
 
1560
                    pinfo->pspec != NULL ? "regular" : "custom",
 
1561
                    name);
 
1562
 
 
1563
      if (parse_custom_node)
 
1564
        res = iface->parse_custom_node (scriptable, script, &value,
 
1565
                                        name,
 
1566
                                        pinfo->node);
 
1567
 
 
1568
      if (!res)
 
1569
        res = clutter_script_parse_node (script, &value,
 
1570
                                         name,
 
1571
                                         pinfo->node,
 
1572
                                         pinfo->pspec);
 
1573
 
 
1574
      if (!res)
 
1575
        {
 
1576
          CLUTTER_NOTE (SCRIPT, "Child property '%s' ignored", name);
 
1577
          unresolved = g_list_prepend (unresolved, pinfo);
 
1578
          continue;
 
1579
        }
 
1580
 
 
1581
      
 
1582
      CLUTTER_NOTE (SCRIPT,
 
1583
                    "Setting %s child property '%s' (type:%s) to "
 
1584
                    "object '%s' (id:%s)",
 
1585
                    set_custom_property ? "custom" : "regular",
 
1586
                    name,
 
1587
                    g_type_name (G_VALUE_TYPE (&value)),
 
1588
                    g_type_name (oinfo->gtype),
 
1589
                    oinfo->id);
 
1590
 
 
1591
      clutter_container_child_set_property (container, actor,
 
1592
                                            name,
 
1593
                                            &value);
 
1594
 
 
1595
      g_value_unset (&value);
 
1596
 
 
1597
      property_info_free (pinfo);
 
1598
    }
 
1599
 
 
1600
  g_list_free (properties);
 
1601
 
 
1602
  oinfo->properties = unresolved;
 
1603
}
 
1604
 
 
1605
static void
 
1606
apply_behaviours (ClutterScript *script,
 
1607
                  ObjectInfo    *oinfo)
 
1608
{
 
1609
  ClutterActor *actor = CLUTTER_ACTOR (oinfo->object);
 
1610
  GList *l, *unresolved;
 
1611
 
 
1612
  unresolved = NULL;
 
1613
  for (l = oinfo->behaviours; l != NULL; l = l->next)
 
1614
    {
 
1615
      const gchar *name = l->data;
 
1616
      ObjectInfo *behaviour_info;
 
1617
      GObject *object = NULL;
 
1618
 
 
1619
      behaviour_info = _clutter_script_get_object_info (script, name);
 
1620
      if (behaviour_info != NULL)
 
1621
        {
 
1622
          _clutter_script_construct_object (script, behaviour_info);
 
1623
          object = behaviour_info->object;
 
1624
        }
 
1625
 
 
1626
      if (object == NULL)
 
1627
        {
 
1628
          unresolved = g_list_prepend (unresolved, g_strdup (name));
 
1629
          continue;
 
1630
        }
 
1631
 
 
1632
      CLUTTER_NOTE (SCRIPT, "Applying behaviour '%s' to actor of type '%s'",
 
1633
                    name,
 
1634
                    g_type_name (G_OBJECT_TYPE (actor)));
 
1635
 
 
1636
      clutter_behaviour_apply (CLUTTER_BEHAVIOUR (object), actor);
 
1637
    }
 
1638
 
 
1639
  g_list_foreach (oinfo->behaviours, (GFunc) g_free, NULL);
 
1640
  g_list_free (oinfo->behaviours);
 
1641
 
 
1642
  oinfo->behaviours = unresolved;
 
1643
}
 
1644
 
 
1645
static void
 
1646
add_children (ClutterScript *script,
 
1647
              ObjectInfo    *oinfo)
 
1648
{
 
1649
  ClutterContainer *container = CLUTTER_CONTAINER (oinfo->object);
 
1650
  GList *l, *unresolved;
 
1651
 
 
1652
  unresolved = NULL;
 
1653
  for (l = oinfo->children; l != NULL; l = l->next)
 
1654
    {
 
1655
      const gchar *name = l->data;
 
1656
      GObject *object = NULL;
 
1657
      ObjectInfo *child_info;
 
1658
 
 
1659
      child_info = _clutter_script_get_object_info (script, name);
 
1660
      if (child_info != NULL)
 
1661
        {
 
1662
          _clutter_script_construct_object (script, child_info);
 
1663
          object = child_info->object;
 
1664
        }
 
1665
 
 
1666
      if (object == NULL)
 
1667
        {
 
1668
          unresolved = g_list_prepend (unresolved, g_strdup (name));
 
1669
          continue;
 
1670
        }
 
1671
 
 
1672
      if (!CLUTTER_IS_ACTOR (object))
 
1673
        {
 
1674
          g_warning ("The object definition '%s' (type: %s) is not "
 
1675
                     "an actor, but it is referenced in the 'children' "
 
1676
                     "member of the container '%s' (type: %s); skipping.",
 
1677
                     child_info->id,
 
1678
                     g_type_name (child_info->gtype),
 
1679
                     oinfo->id,
 
1680
                     g_type_name (oinfo->gtype));
 
1681
          continue;
 
1682
        }
 
1683
 
 
1684
      CLUTTER_NOTE (SCRIPT, "Adding children '%s' to actor of type '%s'",
 
1685
                    name,
 
1686
                    g_type_name (G_OBJECT_TYPE (container)));
 
1687
 
 
1688
      clutter_container_add_actor (container, CLUTTER_ACTOR (object));
 
1689
 
 
1690
      apply_child_properties (script,
 
1691
                              container, CLUTTER_ACTOR (object),
 
1692
                              child_info);
 
1693
    }
 
1694
 
 
1695
  g_list_foreach (oinfo->children, (GFunc) g_free, NULL);
 
1696
  g_list_free (oinfo->children);
 
1697
 
 
1698
  oinfo->children = unresolved;
 
1699
}
 
1700
 
 
1701
static inline void
 
1702
_clutter_script_check_unresolved (ClutterScript *script,
 
1703
                                  ObjectInfo    *oinfo)
 
1704
{
 
1705
  if (oinfo->children != NULL && CLUTTER_IS_CONTAINER (oinfo->object))
 
1706
    add_children (script, oinfo);
 
1707
 
 
1708
  if (oinfo->behaviours != NULL && CLUTTER_IS_ACTOR (oinfo->object))
 
1709
    apply_behaviours (script, oinfo);
 
1710
 
 
1711
  if (oinfo->properties || oinfo->children || oinfo->behaviours)
 
1712
    oinfo->has_unresolved = TRUE;
 
1713
  else
 
1714
    oinfo->has_unresolved = FALSE;
 
1715
}
 
1716
 
 
1717
void
 
1718
_clutter_script_apply_properties (ClutterScript *script,
 
1719
                                  ObjectInfo    *oinfo)
 
1720
{
 
1721
  ClutterScriptable *scriptable = NULL;
 
1722
  ClutterScriptableIface *iface = NULL;
 
1723
  gboolean set_custom_property = FALSE;
 
1724
  GObject *object = oinfo->object;
 
1725
  GList *properties;
 
1726
  GArray *params;
 
1727
  guint i;
 
1728
 
 
1729
  if (!oinfo->has_unresolved)
 
1730
    return;
 
1731
 
 
1732
  /* shortcut, to avoid typechecking every time */
 
1733
  if (CLUTTER_IS_SCRIPTABLE (object))
 
1734
    {
 
1735
      scriptable = CLUTTER_SCRIPTABLE (object);
 
1736
      iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
 
1737
 
 
1738
      if (iface->set_custom_property)
 
1739
        set_custom_property = TRUE;
 
1740
    }
 
1741
 
 
1742
  /* then we get the rest of the parameters, asking the object itself
 
1743
   * to translate them for us, if we cannot do that
 
1744
   */
 
1745
  properties = oinfo->properties;
 
1746
  oinfo->properties = clutter_script_translate_parameters (script,
 
1747
                                                           object,
 
1748
                                                           oinfo->id,
 
1749
                                                           properties,
 
1750
                                                           &params);
 
1751
 
 
1752
  /* consume all the properties we could translate in this pass */
 
1753
  for (i = 0; i < params->len; i++)
 
1754
    {
 
1755
      GParameter *param = &g_array_index (params, GParameter, i);
 
1756
 
 
1757
      CLUTTER_NOTE (SCRIPT,
 
1758
                    "Setting %s property '%s' (type:%s) to object '%s' (id:%s)",
 
1759
                    set_custom_property ? "custom" : "regular",
 
1760
                    param->name,
 
1761
                    g_type_name (G_VALUE_TYPE (&param->value)),
 
1762
                    g_type_name (oinfo->gtype),
 
1763
                    oinfo->id);
 
1764
 
 
1765
      if (set_custom_property)
 
1766
        iface->set_custom_property (scriptable, script,
 
1767
                                    param->name,
 
1768
                                    &param->value);
 
1769
      else
 
1770
        g_object_set_property (object, param->name, &param->value);
 
1771
 
 
1772
      g_free ((gchar *) param->name);
 
1773
      g_value_unset (&param->value);
 
1774
    }
 
1775
 
 
1776
  g_array_free (params, TRUE);
 
1777
 
 
1778
  _clutter_script_check_unresolved (script, oinfo);
 
1779
}
 
1780
 
 
1781
void
 
1782
_clutter_script_construct_object (ClutterScript *script,
 
1783
                                  ObjectInfo    *oinfo)
 
1784
{
 
1785
  GArray *params;
 
1786
  guint i;
 
1787
 
 
1788
  /* we have completely updated the object */
 
1789
  if (oinfo->object != NULL)
 
1790
    {
 
1791
      if (oinfo->has_unresolved)
 
1792
        _clutter_script_check_unresolved (script, oinfo);
 
1793
 
 
1794
      return;
 
1795
    }
 
1796
 
 
1797
  if (oinfo->gtype == G_TYPE_INVALID)
 
1798
    {
 
1799
      if (G_UNLIKELY (oinfo->type_func))
 
1800
        oinfo->gtype = clutter_script_get_type_from_symbol (oinfo->type_func);
 
1801
      else
 
1802
        oinfo->gtype = clutter_script_get_type_from_name (script, oinfo->class_name);
 
1803
 
 
1804
      if (G_UNLIKELY (oinfo->gtype == G_TYPE_INVALID))
 
1805
        return;
 
1806
 
 
1807
      oinfo->is_toplevel =
 
1808
        g_type_is_a (oinfo->gtype, G_TYPE_INITIALLY_UNOWNED)
 
1809
          ? FALSE
 
1810
          : TRUE;
 
1811
    }
 
1812
 
 
1813
  if (oinfo->gtype == CLUTTER_TYPE_STAGE && oinfo->is_stage_default)
 
1814
    {
 
1815
      GList *properties = oinfo->properties;
 
1816
 
 
1817
      /* the default stage is a complex beast: we cannot create it using
 
1818
       * g_object_newv() but we need clutter_script_construct_parameters()
 
1819
       * to add the GParamSpec to the PropertyInfo pspec member, so
 
1820
       * that we don't have to implement every complex property (like
 
1821
       * the "color" one) directly inside the ClutterStage class.
 
1822
       */
 
1823
      oinfo->properties =
 
1824
        clutter_script_construct_parameters (script,
 
1825
                                             oinfo->gtype,
 
1826
                                             oinfo->id,
 
1827
                                             properties,
 
1828
                                             &params);
 
1829
 
 
1830
      oinfo->object = G_OBJECT (clutter_stage_get_default ());
 
1831
 
 
1832
      for (i = 0; i < params->len; i++)
 
1833
        {
 
1834
          GParameter *param = &g_array_index (params, GParameter, i);
 
1835
 
 
1836
          g_free ((gchar *) param->name);
 
1837
          g_value_unset (&param->value);
 
1838
        }
 
1839
 
 
1840
      g_array_free (params, TRUE);
 
1841
    }
 
1842
  else
 
1843
    {
 
1844
      GList *properties = oinfo->properties;
 
1845
 
 
1846
      /* every other object: first, we get the construction parameters */
 
1847
      oinfo->properties =
 
1848
        clutter_script_construct_parameters (script,
 
1849
                                             oinfo->gtype,
 
1850
                                             oinfo->id,
 
1851
                                             properties,
 
1852
                                             &params);
 
1853
 
 
1854
      oinfo->object = g_object_newv (oinfo->gtype,
 
1855
                                     params->len,
 
1856
                                     (GParameter *) params->data);
 
1857
 
 
1858
      for (i = 0; i < params->len; i++)
 
1859
        {
 
1860
          GParameter *param = &g_array_index (params, GParameter, i);
 
1861
 
 
1862
          g_free ((gchar *) param->name);
 
1863
          g_value_unset (&param->value);
 
1864
        }
 
1865
 
 
1866
      g_array_free (params, TRUE);
 
1867
   }
 
1868
 
 
1869
  g_assert (oinfo->object != NULL);
 
1870
 
 
1871
  if (CLUTTER_IS_SCRIPTABLE (oinfo->object))
 
1872
    clutter_scriptable_set_id (CLUTTER_SCRIPTABLE (oinfo->object), oinfo->id);
 
1873
  else
 
1874
    g_object_set_data_full (oinfo->object, "clutter-script-id",
 
1875
                            g_strdup (oinfo->id),
 
1876
                            g_free);
 
1877
 
 
1878
  _clutter_script_check_unresolved (script, oinfo);
 
1879
}