~oem-solutions-group/unity-2d/clutter-1.0

« back to all changes in this revision

Viewing changes to clutter/clutter-script.c

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2010-03-21 13:27:56 UTC
  • mto: (2.1.3 experimental)
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: james.westby@ubuntu.com-20100321132756-nf8yd30yxo3zzwcm
Tags: upstream-1.2.2
ImportĀ upstreamĀ versionĀ 1.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 * Lesser General Public License for more details.
19
19
 *
20
20
 * You should have received a copy of the GNU Lesser General Public
21
 
 * License along with this library; if not, write to the
22
 
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23
 
 * Boston, MA 02111-1307, USA.
 
21
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 
22
 *
 
23
 *
24
24
 */
25
25
 
26
26
/**
86
86
 *   "angle-end"   : 360.0,
87
87
 *   "axis"        : "z-axis",
88
88
 *   "alpha"       : {
89
 
 *     "timeline" : { "duration" : 4000, "fps" : 60, "loop" : true },
90
 
 *     "function" : "sine"
 
89
 *     "timeline" : { "duration" : 4000, "loop" : true },
 
90
 *     "mode"     : "easeInSine"
91
91
 *   }
92
92
 * }
93
93
 * ]|
105
105
 * ]|
106
106
 *
107
107
 * A #ClutterAlpha belonging to a #ClutterBehaviour can only be defined
108
 
 * implicitely. A #ClutterTimeline belonging to a #ClutterAlpha can be
109
 
 * either defined implicitely or explicitely. Implicitely defined
110
 
 * #ClutterAlpha<!-- -->s and #ClutterTimeline<!-- -->s can omit the
111
 
 * <varname>id</varname> member, as well as the <varname>type</varname> member,
112
 
 * but will not be available using clutter_script_get_object() (they can,
113
 
 * however, be extracted using the #ClutterBehaviour and #ClutterAlpha
114
 
 * API respectively).
 
108
 * implicitly like in the example above, or explicitly by setting the
 
109
 * "alpha" property to point to a previously defined #ClutterAlpha, e.g.:
 
110
 *
 
111
 * |[
 
112
 * {
 
113
 *   "id"          : "rotate-behaviour",
 
114
 *   "type"        : "ClutterBehaviourRotate",
 
115
 *   "angle-start" : 0.0,
 
116
 *   "angle-end"   : 360.0,
 
117
 *   "axis"        : "z-axis",
 
118
 *   "alpha"       : {
 
119
 *     "id"       : "rotate-alpha",
 
120
 *     "type"     : "ClutterAlpha",
 
121
 *     "timeline" : {
 
122
 *       "id"       : "rotate-timeline",
 
123
 *       "type      : "ClutterTimeline",
 
124
 *       "duration" : 4000,
 
125
 *       "loop"     : true
 
126
 *     },
 
127
 *     "function" : "custom_sine_alpha"
 
128
 *   }
 
129
 * }
 
130
 * ]|
 
131
 *
 
132
 * Implicitely defined #ClutterAlpha<!-- -->s and #ClutterTimeline<!-- -->s
 
133
 * can omit the <varname>id</varname> member, as well as the
 
134
 * <varname>type</varname> member, but will not be available using
 
135
 * clutter_script_get_object() (they can, however, be extracted using the
 
136
 * #ClutterBehaviour and #ClutterAlpha API respectively).
115
137
 *
116
138
 * Signal handlers can be defined inside a Clutter UI definition file and
117
139
 * then autoconnected to their respective signals using the
208
230
  guint last_merge_id;
209
231
  guint last_unknown;
210
232
 
211
 
  JsonParser *parser;
 
233
  ClutterScriptParser *parser;
212
234
 
213
235
  gchar **search_paths;
214
236
 
218
240
 
219
241
G_DEFINE_TYPE (ClutterScript, clutter_script, G_TYPE_OBJECT);
220
242
 
221
 
static void
222
 
warn_missing_attribute (ClutterScript *script,
223
 
                        const gchar   *id,
224
 
                        const gchar   *attribute)
225
 
{
226
 
  ClutterScriptPrivate *priv = script->priv;
227
 
 
228
 
  if (G_LIKELY (id))
229
 
    {
230
 
      g_warning ("%s:%d: object '%s' has no '%s' attribute",
231
 
                 priv->is_filename ? priv->filename : "<input>",
232
 
                 json_parser_get_current_line (priv->parser),
233
 
                 id,
234
 
                 attribute);
235
 
    }
236
 
  else
237
 
    {
238
 
      g_warning ("%s:%d: object has no '%s' attribute",
239
 
                 priv->is_filename ? priv->filename : "<input>",
240
 
                 json_parser_get_current_line (priv->parser),
241
 
                 attribute);
242
 
    }
243
 
}
244
 
 
245
 
static void
246
 
warn_invalid_value (ClutterScript *script,
247
 
                    const gchar   *attribute,
248
 
                    const gchar   *expected,
249
 
                    JsonNode      *node)
250
 
{
251
 
  ClutterScriptPrivate *priv = script->priv;
252
 
 
253
 
  if (G_LIKELY (node))
254
 
    {
255
 
      g_warning ("%s:%d: invalid value of type '%s' for attribute '%s':"
256
 
                 "a value of type '%s' is expected",
257
 
                 priv->is_filename ? priv->filename : "<input>",
258
 
                 json_parser_get_current_line (priv->parser),
259
 
                 json_node_type_name (node),
260
 
                 attribute,
261
 
                 expected);
262
 
    }
263
 
  else
264
 
    {
265
 
      g_warning ("%s:%d: invalid value for attribute '%s':"
266
 
                 "a value of type '%s' is expected",
267
 
                 priv->is_filename ? priv->filename : "<input>",
268
 
                 json_parser_get_current_line (priv->parser),
269
 
                 attribute,
270
 
                 expected);
271
 
    }
272
 
}
273
 
 
274
 
static const gchar *
275
 
get_id_from_node (JsonNode *node)
276
 
{
277
 
  JsonObject *object;
278
 
 
279
 
  switch (JSON_NODE_TYPE (node))
280
 
    {
281
 
    case JSON_NODE_OBJECT:
282
 
      object = json_node_get_object (node);
283
 
      if (json_object_has_member (object, "id"))
284
 
        {
285
 
          JsonNode *id = json_object_get_member (object, "id");
286
 
 
287
 
          return json_node_get_string (id);
288
 
        }
289
 
      break;
290
 
 
291
 
    case JSON_NODE_VALUE:
292
 
      return json_node_get_string (node);
293
 
 
294
 
    default:
295
 
      break;
296
 
    }
297
 
 
298
 
  return NULL;
299
 
}
300
 
 
301
 
static GList *
302
 
parse_children (ObjectInfo *oinfo,
303
 
                JsonNode   *node)
304
 
{
305
 
  JsonArray *array;
306
 
  GList *retval;
307
 
  guint array_len, i;
308
 
 
309
 
  if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
310
 
    return NULL;
311
 
 
312
 
  retval = oinfo->children;
313
 
 
314
 
  array = json_node_get_array (node);
315
 
  array_len = json_array_get_length (array);
316
 
 
317
 
  for (i = 0; i < array_len; i++)
318
 
    {
319
 
      JsonNode *child = json_array_get_element (array, i);
320
 
      const gchar *id;
321
 
 
322
 
      id = get_id_from_node (child);
323
 
      if (id)
324
 
        retval = g_list_prepend (retval, g_strdup (id));
325
 
    }
326
 
 
327
 
  return g_list_reverse (retval);
328
 
}
329
 
 
330
 
static GList *
331
 
parse_signals (ClutterScript *script,
332
 
               ObjectInfo    *oinfo,
333
 
               JsonNode      *node)
334
 
{
335
 
  JsonArray *array;
336
 
  GList *retval;
337
 
  guint array_len, i;
338
 
 
339
 
  if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
340
 
    {
341
 
      warn_invalid_value (script, "signals", "Array", node);
342
 
      return NULL;
343
 
    }
344
 
 
345
 
  retval = oinfo->signals;
346
 
  array = json_node_get_array (node);
347
 
  array_len = json_array_get_length (array);
348
 
 
349
 
  for (i = 0; i < array_len; i++)
350
 
    {
351
 
      JsonNode *val = json_array_get_element (array, i);
352
 
      JsonObject *object;
353
 
      SignalInfo *sinfo;
354
 
      const gchar *name;
355
 
      const gchar *handler;
356
 
      const gchar *connect;
357
 
      GConnectFlags flags = 0;
358
 
 
359
 
      if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT)
360
 
        {
361
 
          warn_invalid_value (script, "signals array", "Object", node);
362
 
          continue;
363
 
        }
364
 
 
365
 
      object = json_node_get_object (val);
366
 
 
367
 
      /* mandatory: "name" */
368
 
      if (!json_object_has_member (object, "name"))
369
 
        {
370
 
          warn_missing_attribute (script, NULL, "name");
371
 
          continue;
372
 
        }
373
 
      else
374
 
        {
375
 
          val = json_object_get_member (object, "name");
376
 
          if ((JSON_NODE_TYPE (val) == JSON_NODE_VALUE) &&
377
 
              json_node_get_string (val) != NULL)
378
 
            name = json_node_get_string (val);
379
 
          else
380
 
            {
381
 
              warn_invalid_value (script, "name", "string", val);
382
 
              continue;
383
 
            }
384
 
        }
385
 
 
386
 
      /* mandatory: "handler" */
387
 
      if (!json_object_has_member (object, "handler"))
388
 
        {
389
 
          warn_missing_attribute (script, NULL, "handler");
390
 
          continue;
391
 
        }
392
 
      else
393
 
        {
394
 
          val = json_object_get_member (object, "handler");
395
 
          if ((JSON_NODE_TYPE (val) == JSON_NODE_VALUE) &&
396
 
              json_node_get_string (val) != NULL)
397
 
            handler = json_node_get_string (val);
398
 
          else
399
 
            {
400
 
              warn_invalid_value (script, "handler", "string", val);
401
 
              continue;
402
 
            }
403
 
        }
404
 
 
405
 
      /* optional: "object" */
406
 
      if (json_object_has_member (object, "object"))
407
 
        {
408
 
          val = json_object_get_member (object, "object");
409
 
          if ((JSON_NODE_TYPE (val) == JSON_NODE_VALUE) &&
410
 
              json_node_get_string (val) != NULL)
411
 
            connect = json_node_get_string (val);
412
 
          else
413
 
            connect = NULL;
414
 
        }
415
 
      else
416
 
        connect = NULL;
417
 
 
418
 
      /* optional: "after" */
419
 
      if (json_object_has_member (object, "after"))
420
 
        {
421
 
          val = json_object_get_member (object, "after");
422
 
          if (json_node_get_boolean (val))
423
 
            flags |= G_CONNECT_AFTER;
424
 
        }
425
 
 
426
 
      /* optional: "swapped" */
427
 
      if (json_object_has_member (object, "swapped"))
428
 
        {
429
 
          val = json_object_get_member (object, "swapped");
430
 
          if (json_node_get_boolean (val))
431
 
            flags |= G_CONNECT_SWAPPED;
432
 
        }
433
 
 
434
 
      CLUTTER_NOTE (SCRIPT, 
435
 
                    "Parsing signal '%s' (handler:%s, object:%s, flags:%d)",
436
 
                    name,
437
 
                    handler, connect, flags);
438
 
 
439
 
      sinfo = g_slice_new0 (SignalInfo);
440
 
      sinfo->name = g_strdup (name);
441
 
      sinfo->handler = g_strdup (handler);
442
 
      sinfo->object = g_strdup (connect);
443
 
      sinfo->flags = flags;
444
 
 
445
 
      retval = g_list_prepend (retval, sinfo);
446
 
    }
447
 
 
448
 
  return retval;
449
 
}
450
 
 
451
 
static GList *
452
 
parse_behaviours (ObjectInfo *oinfo,
453
 
                  JsonNode   *node)
454
 
{
455
 
  JsonArray *array;
456
 
  GList *retval;
457
 
  guint array_len, i;
458
 
 
459
 
  if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
460
 
    return NULL;
461
 
 
462
 
  retval = oinfo->behaviours;
463
 
 
464
 
  array = json_node_get_array (node);
465
 
  array_len = json_array_get_length (array);
466
 
 
467
 
  for (i = 0; i < array_len; i++)
468
 
    {
469
 
      JsonNode *child = json_array_get_element (array, i);
470
 
      const gchar *id;
471
 
 
472
 
      id = get_id_from_node (child);
473
 
      if (id)
474
 
        retval = g_list_prepend (retval, g_strdup (id));
475
 
    }
476
 
 
477
 
  return g_list_reverse (retval);
478
 
}
479
 
 
480
 
static ClutterTimeline *
481
 
construct_timeline (ClutterScript *script,
482
 
                    JsonObject    *object)
483
 
{
484
 
  ClutterTimeline *retval = NULL;
485
 
  ObjectInfo *oinfo;
486
 
  GList *members, *l;
487
 
  
488
 
  /* we fake an ObjectInfo so we can reuse clutter_script_construct_object()
489
 
   * here; we do not save it inside the hash table, because if this had
490
 
   * been a named object then we wouldn't have ended up here in the first
491
 
   * place
492
 
   */
493
 
  oinfo = g_slice_new0 (ObjectInfo);
494
 
  oinfo->gtype = CLUTTER_TYPE_TIMELINE;
495
 
  oinfo->id = g_strdup ("dummy");
496
 
 
497
 
  members = json_object_get_members (object);
498
 
  for (l = members; l != NULL; l = l->next)
499
 
    {
500
 
      const gchar *name = l->data;
501
 
      JsonNode *node = json_object_get_member (object, name);
502
 
      PropertyInfo *pinfo = g_slice_new0 (PropertyInfo);
503
 
 
504
 
      pinfo->name = g_strdelimit (g_strdup (name), G_STR_DELIMITERS, '-');
505
 
      pinfo->node = node;
506
 
 
507
 
      oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
508
 
    }
509
 
 
510
 
  g_list_free (members);
511
 
  
512
 
  retval = CLUTTER_TIMELINE (clutter_script_construct_object (script, oinfo));
513
 
 
514
 
  /* we transfer ownership to the alpha function later */
515
 
  oinfo->is_toplevel = FALSE;
516
 
  object_info_free (oinfo);
517
 
 
518
 
  return retval;
519
 
}
520
 
 
521
 
/* define the names of the animation modes to match the ones
522
 
 * that developers might be more accustomed to
523
 
 */
524
 
static const struct
525
 
{
526
 
  const gchar *name;
527
 
  ClutterAnimationMode mode;
528
 
} animation_modes[] = {
529
 
  { "linear", CLUTTER_LINEAR },
530
 
  { "easeInQuad", CLUTTER_EASE_IN_QUAD },
531
 
  { "easeOutQuad", CLUTTER_EASE_OUT_QUAD },
532
 
  { "easeInOutQuad", CLUTTER_EASE_IN_OUT_QUAD },
533
 
  { "easeInCubic", CLUTTER_EASE_IN_CUBIC },
534
 
  { "easeOutCubic", CLUTTER_EASE_OUT_CUBIC },
535
 
  { "easeInOutCubic", CLUTTER_EASE_IN_OUT_CUBIC },
536
 
  { "easeInQuart", CLUTTER_EASE_IN_QUART },
537
 
  { "easeOutQuart", CLUTTER_EASE_OUT_QUART },
538
 
  { "easeInOutQuart", CLUTTER_EASE_IN_OUT_QUART },
539
 
  { "easeInQuint", CLUTTER_EASE_IN_QUINT },
540
 
  { "easeOutQuint", CLUTTER_EASE_OUT_QUINT },
541
 
  { "easeInOutQuint", CLUTTER_EASE_IN_OUT_QUINT },
542
 
  { "easeInSine", CLUTTER_EASE_IN_SINE },
543
 
  { "easeOutSine", CLUTTER_EASE_OUT_SINE },
544
 
  { "easeInOutSine", CLUTTER_EASE_IN_OUT_SINE },
545
 
  { "easeInExpo", CLUTTER_EASE_IN_EXPO },
546
 
  { "easeOutExpo", CLUTTER_EASE_OUT_EXPO },
547
 
  { "easeInOutExpo", CLUTTER_EASE_IN_OUT_EXPO },
548
 
  { "easeInCirc", CLUTTER_EASE_IN_CIRC },
549
 
  { "easeOutCirc", CLUTTER_EASE_OUT_CIRC },
550
 
  { "easeInOutCirc", CLUTTER_EASE_IN_OUT_CIRC },
551
 
  { "easeInElastic", CLUTTER_EASE_IN_ELASTIC },
552
 
  { "easeOutElastic", CLUTTER_EASE_OUT_ELASTIC },
553
 
  { "easeInOutElastic", CLUTTER_EASE_IN_OUT_ELASTIC },
554
 
  { "easeInBack", CLUTTER_EASE_IN_BACK },
555
 
  { "easeOutBack", CLUTTER_EASE_OUT_BACK },
556
 
  { "easeInOutBack", CLUTTER_EASE_IN_OUT_BACK },
557
 
  { "easeInBounce", CLUTTER_EASE_IN_BOUNCE },
558
 
  { "easeOutBounce", CLUTTER_EASE_OUT_BOUNCE },
559
 
  { "easeInOutBounce", CLUTTER_EASE_IN_OUT_BOUNCE },
560
 
};
561
 
 
562
 
static const gint n_animation_modes = G_N_ELEMENTS (animation_modes);
563
 
 
564
 
static ClutterAnimationMode
565
 
resolve_animation_mode (const gchar *name)
566
 
{
567
 
  gint i, res = 0;
568
 
 
569
 
  for (i = 0; i < n_animation_modes; i++)
570
 
    {
571
 
      if (strcmp (animation_modes[i].name, name) == 0)
572
 
        return animation_modes[i].mode;
573
 
    }
574
 
 
575
 
  if (clutter_script_enum_from_string (CLUTTER_TYPE_ANIMATION_MODE,
576
 
                                       name, &res))
577
 
    return res;
578
 
 
579
 
  g_warning ("Unable to find the animation mode '%s'", name);
580
 
 
581
 
  return CLUTTER_CUSTOM_MODE;
582
 
}
583
 
 
584
 
static ClutterAlphaFunc
585
 
resolve_alpha_func (const gchar *name)
586
 
{
587
 
  static GModule *module = NULL;
588
 
  ClutterAlphaFunc func;
589
 
 
590
 
  CLUTTER_NOTE (SCRIPT, "Looking up '%s' alpha function", name);
591
 
 
592
 
  if (G_UNLIKELY (!module))
593
 
    module = g_module_open (NULL, G_MODULE_BIND_LAZY);
594
 
 
595
 
  if (g_module_symbol (module, name, (gpointer) &func))
596
 
    {
597
 
      CLUTTER_NOTE (SCRIPT, "Found '%s' alpha function in the symbols table",
598
 
                    name);
599
 
      return func;
600
 
    }
601
 
 
602
 
  return NULL;
603
 
}
604
 
 
605
 
GObject *
606
 
clutter_script_parse_alpha (ClutterScript *script,
607
 
                            JsonNode      *node)
608
 
{
609
 
  GObject *retval = NULL;
610
 
  JsonObject *object;
611
 
  ClutterTimeline *timeline = NULL;
612
 
  ClutterAlphaFunc alpha_func = NULL;
613
 
  ClutterAnimationMode mode = CLUTTER_CUSTOM_MODE;
614
 
  JsonNode *val;
615
 
  gboolean unref_timeline = FALSE;
616
 
 
617
 
  if (JSON_NODE_TYPE (node) != JSON_NODE_OBJECT)
618
 
    return NULL;
619
 
 
620
 
  object = json_node_get_object (node);
621
 
  
622
 
  val = json_object_get_member (object, "timeline");
623
 
  if (val)
624
 
    {
625
 
      if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE &&
626
 
          json_node_get_string (val) != NULL)
627
 
        {
628
 
          const gchar *id = json_node_get_string (val);
629
 
 
630
 
          timeline =
631
 
            CLUTTER_TIMELINE (clutter_script_get_object (script, id));
632
 
        }
633
 
      else if (JSON_NODE_TYPE (val) == JSON_NODE_OBJECT)
634
 
        {
635
 
          timeline = construct_timeline (script, json_node_get_object (val));
636
 
          unref_timeline = TRUE;
637
 
        }
638
 
    }
639
 
 
640
 
  val = json_object_get_member (object, "mode");
641
 
  if (val && json_node_get_string (val) != NULL)
642
 
    mode = resolve_animation_mode (json_node_get_string (val));
643
 
 
644
 
  if (mode == CLUTTER_CUSTOM_MODE)
645
 
    {
646
 
      val = json_object_get_member (object, "function");
647
 
      if (val && json_node_get_string (val) != NULL)
648
 
        {
649
 
          alpha_func = resolve_alpha_func (json_node_get_string (val));
650
 
          if (!alpha_func)
651
 
            {
652
 
              g_warning ("Unable to find the function '%s' in the "
653
 
                         "Clutter alpha functions or the symbols table",
654
 
                         json_node_get_string (val));
655
 
            }
656
 
        }
657
 
    }
658
 
 
659
 
  CLUTTER_NOTE (SCRIPT, "Parsed alpha: %s timeline (%p) (mode:%d, func:%p)",
660
 
                unref_timeline ? "implicit" : "explicit",
661
 
                timeline ? timeline : 0x0,
662
 
                mode != CLUTTER_CUSTOM_MODE ? mode : 0,
663
 
                alpha_func ? alpha_func : 0x0);
664
 
 
665
 
  retval = g_object_new (CLUTTER_TYPE_ALPHA, NULL);
666
 
 
667
 
  if (mode != CLUTTER_CUSTOM_MODE)
668
 
    clutter_alpha_set_mode (CLUTTER_ALPHA (retval), mode);
669
 
 
670
 
  if (alpha_func != NULL)
671
 
    clutter_alpha_set_func (CLUTTER_ALPHA (retval), alpha_func, NULL, NULL);
672
 
 
673
 
  clutter_alpha_set_timeline (CLUTTER_ALPHA (retval), timeline);
674
 
  if (unref_timeline)
675
 
    g_object_unref (timeline);
676
 
 
677
 
  return retval;
678
 
}
679
 
 
680
 
static void
681
 
json_object_end (JsonParser *parser,
682
 
                 JsonObject *object,
683
 
                 gpointer    user_data)
684
 
{
685
 
  ClutterScript *script = user_data;
686
 
  ClutterScriptPrivate *priv = script->priv;
687
 
  ObjectInfo *oinfo;
688
 
  JsonNode *val;
689
 
  const gchar *id;
690
 
  GList *members, *l;
691
 
 
692
 
  if (!json_object_has_member (object, "id"))
693
 
    {
694
 
      gchar *fake;
695
 
 
696
 
      if (!json_object_has_member (object, "type"))
697
 
        return;
698
 
 
699
 
      fake = g_strdup_printf ("script-%d-%d",
700
 
                              priv->last_merge_id,
701
 
                              priv->last_unknown++);
702
 
 
703
 
      val = json_node_new (JSON_NODE_VALUE);
704
 
      json_node_set_string (val, fake);
705
 
      json_object_set_member (object, "id", val);
706
 
 
707
 
      g_free (fake);
708
 
    }
709
 
      
710
 
  if (!json_object_has_member (object, "type"))
711
 
    {
712
 
      val = json_object_get_member (object, "id");
713
 
 
714
 
      warn_missing_attribute (script, json_node_get_string (val), "type");
715
 
      return;
716
 
    }
717
 
 
718
 
  val = json_object_get_member (object, "id");
719
 
  id = json_node_get_string (val);
720
 
 
721
 
  oinfo = g_hash_table_lookup (priv->objects, id);
722
 
  if (G_LIKELY (!oinfo))
723
 
    {
724
 
      oinfo = g_slice_new0 (ObjectInfo);
725
 
      oinfo->merge_id = priv->last_merge_id;
726
 
      oinfo->id = g_strdup (id);
727
 
 
728
 
      val = json_object_get_member (object, "type");
729
 
      oinfo->class_name = json_node_dup_string (val);
730
 
  
731
 
      if (json_object_has_member (object, "type_func"))
732
 
        {
733
 
          val = json_object_get_member (object, "type_func");
734
 
          oinfo->type_func = json_node_dup_string (val);
735
 
 
736
 
          json_object_remove_member (object, "type_func");
737
 
        }
738
 
    }
739
 
 
740
 
  if (json_object_has_member (object, "children"))
741
 
    {
742
 
      val = json_object_get_member (object, "children");
743
 
      oinfo->children = parse_children (oinfo, val);
744
 
 
745
 
      json_object_remove_member (object, "children");
746
 
    }
747
 
 
748
 
  if (json_object_has_member (object, "behaviours"))
749
 
    {
750
 
      val = json_object_get_member (object, "behaviours");
751
 
      oinfo->behaviours = parse_behaviours (oinfo, val);
752
 
 
753
 
      json_object_remove_member (object, "behaviours");
754
 
    }
755
 
 
756
 
  if (json_object_has_member (object, "signals"))
757
 
    {
758
 
      val = json_object_get_member (object, "signals");
759
 
      oinfo->signals = parse_signals (script, oinfo, val);
760
 
 
761
 
      json_object_remove_member (object, "signals");
762
 
    }
763
 
 
764
 
  if (strcmp (oinfo->class_name, "ClutterStage") == 0 &&
765
 
      json_object_has_member (object, "is-default"))
766
 
    {
767
 
      val = json_object_get_member (object, "is-default");
768
 
      oinfo->is_stage_default = json_node_get_boolean (val);
769
 
 
770
 
      json_object_remove_member (object, "is-default");
771
 
    }
772
 
  else
773
 
    oinfo->is_stage_default = FALSE;
774
 
 
775
 
  oinfo->is_toplevel = FALSE;
776
 
  oinfo->is_unmerged = FALSE;
777
 
  oinfo->has_unresolved = TRUE;
778
 
 
779
 
  members = json_object_get_members (object);
780
 
  for (l = members; l; l = l->next)
781
 
    {
782
 
      const gchar *name = l->data;
783
 
      PropertyInfo *pinfo;
784
 
      JsonNode *node;
785
 
 
786
 
      /* we have already parsed these */
787
 
      if (strcmp (name, "id") == 0 || strcmp (name, "type") == 0)
788
 
        continue;
789
 
 
790
 
      node = json_object_get_member (object, name);
791
 
 
792
 
      pinfo = g_slice_new (PropertyInfo);
793
 
 
794
 
      pinfo->name = g_strdup (name);
795
 
      pinfo->node = node;
796
 
      pinfo->pspec = NULL;
797
 
 
798
 
      oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
799
 
    }
800
 
 
801
 
  g_list_free (members);
802
 
 
803
 
  CLUTTER_NOTE (SCRIPT,
804
 
                "Added object '%s' (type:%s, id:%d, props:%d, signals:%d)",
805
 
                oinfo->id,
806
 
                oinfo->class_name,
807
 
                oinfo->merge_id,
808
 
                g_list_length (oinfo->properties),
809
 
                g_list_length (oinfo->signals));
810
 
 
811
 
  g_hash_table_steal (priv->objects, oinfo->id);
812
 
  g_hash_table_insert (priv->objects, oinfo->id, oinfo);
813
 
 
814
 
  oinfo->object = clutter_script_construct_object (script, oinfo);
815
 
}
816
 
 
817
 
gboolean
818
 
clutter_script_parse_node (ClutterScript *script,
819
 
                           GValue        *value,
820
 
                           const gchar   *name,
821
 
                           JsonNode      *node,
822
 
                           GParamSpec    *pspec)
823
 
{
824
 
  GValue node_value = { 0, };
825
 
  gboolean retval = FALSE;
826
 
 
827
 
  g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE);
828
 
  g_return_val_if_fail (name != NULL, FALSE);
829
 
  g_return_val_if_fail (node != NULL, FALSE);
830
 
 
831
 
  switch (JSON_NODE_TYPE (node))
832
 
    {
833
 
    case JSON_NODE_OBJECT:
834
 
      if (!pspec)
835
 
        return FALSE;
836
 
      else
837
 
        {
838
 
          g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
839
 
 
840
 
          if (G_VALUE_HOLDS (value, CLUTTER_TYPE_ALPHA))
841
 
            {
842
 
              GObject *alpha;
843
 
 
844
 
              alpha = clutter_script_parse_alpha (script, node);
845
 
              if (alpha)
846
 
                {
847
 
                  g_value_set_object (value, alpha);
848
 
                  return TRUE;
849
 
                }
850
 
            }
851
 
          else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_KNOT))
852
 
            {
853
 
              ClutterKnot knot = { 0, };
854
 
 
855
 
              /* knot := { "x" : (int), "y" : (int) } */
856
 
 
857
 
              if (clutter_script_parse_knot (script, node, &knot))
858
 
                {
859
 
                  g_value_set_boxed (value, &knot);
860
 
                  return TRUE;
861
 
                }
862
 
            }
863
 
          else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_GEOMETRY))
864
 
            {
865
 
              ClutterGeometry geom = { 0, };
866
 
 
867
 
              /* geometry := {
868
 
               *        "x" : (int),
869
 
               *        "y" : (int),
870
 
               *        "width" : (int),
871
 
               *        "height" : (int)
872
 
               * }
873
 
               */
874
 
 
875
 
              if (clutter_script_parse_geometry (script, node, &geom))
876
 
                {
877
 
                  g_value_set_boxed (value, &geom);
878
 
                  return TRUE;
879
 
                }
880
 
            }
881
 
          else if (CLUTTER_VALUE_HOLDS_COLOR (value))
882
 
            {
883
 
              ClutterColor color = { 0, };
884
 
 
885
 
              /* color := {
886
 
               *        "red" : (int),
887
 
               *        "green" : (int),
888
 
               *        "blue" : (int),
889
 
               *        "alpha" : (int)
890
 
               * }
891
 
               */
892
 
 
893
 
              if (clutter_script_parse_color (script, node, &color))
894
 
                {
895
 
                  g_value_set_boxed (value, &color);
896
 
                  return TRUE;
897
 
                }
898
 
            }
899
 
         }
900
 
      return FALSE;
901
 
 
902
 
    case JSON_NODE_ARRAY:
903
 
      if (!pspec)
904
 
        return FALSE;
905
 
      else
906
 
        {
907
 
          g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
908
 
 
909
 
          if (G_VALUE_HOLDS (value, CLUTTER_TYPE_KNOT))
910
 
            {
911
 
              ClutterKnot knot = { 0, };
912
 
 
913
 
              /* knot := [ (int), (int) ] */
914
 
 
915
 
              if (clutter_script_parse_knot (script, node, &knot))
916
 
                {
917
 
                  g_value_set_boxed (value, &knot);
918
 
                  return TRUE;
919
 
                }
920
 
            }
921
 
          else if (G_VALUE_HOLDS (value, CLUTTER_TYPE_GEOMETRY))
922
 
            {
923
 
              ClutterGeometry geom = { 0, };
924
 
 
925
 
              /* geometry := [ (int), (int), (int), (int) ] */
926
 
 
927
 
              if (clutter_script_parse_geometry (script, node, &geom))
928
 
                {
929
 
                  g_value_set_boxed (value, &geom);
930
 
                  return TRUE;
931
 
                }
932
 
            }
933
 
          else if (CLUTTER_VALUE_HOLDS_COLOR (value))
934
 
            {
935
 
              ClutterColor color = { 0, };
936
 
 
937
 
              /* color := [ (int), (int), (int), (int) ] */
938
 
 
939
 
              if (clutter_script_parse_color (script, node, &color))
940
 
                {
941
 
                  g_value_set_boxed (value, &color);
942
 
                  return TRUE;
943
 
                }
944
 
            }
945
 
          else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
946
 
            {
947
 
              JsonArray *array = json_node_get_array (node);
948
 
              guint i, array_len = json_array_get_length (array);
949
 
              GPtrArray *str_array = g_ptr_array_sized_new (array_len);
950
 
 
951
 
              /* strv := [ (str), (str), ... ] */
952
 
 
953
 
              for (i = 0; i < array_len; i++)
954
 
                {
955
 
                  JsonNode *val = json_array_get_element (array, i);
956
 
 
957
 
                  if (JSON_NODE_TYPE (val) != JSON_NODE_VALUE &&
958
 
                      json_node_get_string (val) == NULL)
959
 
                    continue;
960
 
 
961
 
                  g_ptr_array_add (str_array,
962
 
                                   (gpointer) json_node_get_string (val));
963
 
                }
964
 
 
965
 
              g_value_set_boxed (value, str_array->pdata);
966
 
              g_ptr_array_free (str_array, TRUE);
967
 
 
968
 
              return TRUE;
969
 
            }
970
 
        }
971
 
      return FALSE;
972
 
 
973
 
    case JSON_NODE_NULL:
974
 
      return FALSE;
975
 
 
976
 
    case JSON_NODE_VALUE:
977
 
      json_node_get_value (node, &node_value);
978
 
 
979
 
      if (pspec)
980
 
        g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
981
 
      else
982
 
        g_value_init (value, G_VALUE_TYPE (&node_value));
983
 
 
984
 
      switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)))
985
 
        {
986
 
        /* fundamental JSON types */
987
 
        case G_TYPE_INT:
988
 
        case G_TYPE_DOUBLE:
989
 
        case G_TYPE_STRING:
990
 
        case G_TYPE_BOOLEAN:
991
 
          g_value_copy (&node_value, value);
992
 
          retval = TRUE;
993
 
          break;
994
 
 
995
 
        case G_TYPE_UINT:
996
 
          g_value_set_uint (value, (guint) g_value_get_int (&node_value));
997
 
          retval = TRUE;
998
 
          break;
999
 
 
1000
 
        case G_TYPE_ULONG:
1001
 
          g_value_set_ulong (value, (gulong) g_value_get_int (&node_value));
1002
 
          retval = TRUE;
1003
 
          break;
1004
 
 
1005
 
        case G_TYPE_UCHAR:
1006
 
          g_value_set_uchar (value, (guchar) g_value_get_int (&node_value));
1007
 
          retval = TRUE;
1008
 
          break;
1009
 
 
1010
 
        case G_TYPE_FLOAT:
1011
 
          if (G_VALUE_HOLDS (&node_value, G_TYPE_DOUBLE))
1012
 
            {
1013
 
              g_value_set_float (value, g_value_get_double (&node_value));
1014
 
              retval = TRUE;
1015
 
            }
1016
 
          else if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64))
1017
 
            {
1018
 
              g_value_set_float (value, g_value_get_int64 (&node_value));
1019
 
              retval = TRUE;
1020
 
            }
1021
 
          break;
1022
 
 
1023
 
        case G_TYPE_ENUM:
1024
 
          if (G_VALUE_HOLDS (&node_value, G_TYPE_INT))
1025
 
            {
1026
 
              g_value_set_enum (value, g_value_get_int (&node_value));
1027
 
              retval = TRUE;
1028
 
            }
1029
 
          else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1030
 
            {
1031
 
              gint enum_value;
1032
 
 
1033
 
              retval = clutter_script_enum_from_string (G_VALUE_TYPE (value),
1034
 
                                                        g_value_get_string (&node_value),
1035
 
                                                        &enum_value);
1036
 
              if (retval)
1037
 
                g_value_set_enum (value, enum_value);
1038
 
            }
1039
 
          break;
1040
 
 
1041
 
        case G_TYPE_FLAGS:
1042
 
          if (G_VALUE_HOLDS (&node_value, G_TYPE_INT))
1043
 
            {
1044
 
              g_value_set_flags (value, g_value_get_int (&node_value));
1045
 
              retval = TRUE;
1046
 
            }
1047
 
          else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1048
 
            {
1049
 
              gint flags_value;
1050
 
 
1051
 
              retval = clutter_script_flags_from_string (G_VALUE_TYPE (value),
1052
 
                                                         g_value_get_string (&node_value),
1053
 
                                                         &flags_value);
1054
 
              if (retval)
1055
 
                g_value_set_flags (value, flags_value);
1056
 
            }
1057
 
          break;
1058
 
 
1059
 
        case G_TYPE_BOXED:
1060
 
          if (G_VALUE_HOLDS (value, CLUTTER_TYPE_COLOR))
1061
 
            {
1062
 
              if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1063
 
                {
1064
 
                  const gchar *str = g_value_get_string (&node_value);
1065
 
                  ClutterColor color = { 0, };
1066
 
 
1067
 
                  if (str && str[0] != '\0')
1068
 
                    clutter_color_from_string (&color, str);
1069
 
 
1070
 
                  g_value_set_boxed (value, &color);
1071
 
                  retval = TRUE;
1072
 
                }
1073
 
            }
1074
 
          break;
1075
 
 
1076
 
        case G_TYPE_OBJECT:
1077
 
#ifdef USE_GDKPIXBUF
1078
 
          if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
1079
 
            {
1080
 
              if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1081
 
                {
1082
 
                  const gchar *str = g_value_get_string (&node_value);
1083
 
                  GdkPixbuf *pixbuf = NULL;
1084
 
                  gchar *path;
1085
 
                  GError *error;
1086
 
 
1087
 
                  if (g_path_is_absolute (str))
1088
 
                    path = g_strdup (str);
1089
 
                  else
1090
 
                    {
1091
 
                      gchar *dirname = NULL;
1092
 
 
1093
 
                      if (script->priv->is_filename)
1094
 
                        dirname = g_path_get_dirname (script->priv->filename);
1095
 
                      else
1096
 
                        dirname = g_get_current_dir ();
1097
 
 
1098
 
                      path = g_build_filename (dirname, str, NULL);
1099
 
                      g_free (dirname);
1100
 
                    }
1101
 
 
1102
 
                  error = NULL;
1103
 
                  pixbuf = gdk_pixbuf_new_from_file (path, &error);
1104
 
                  if (error)
1105
 
                    {
1106
 
                      g_warning ("Unable to open image at path '%s': %s",
1107
 
                                 path,
1108
 
                                 error->message);
1109
 
                      g_error_free (error);
1110
 
                    }
1111
 
                  else
1112
 
                    {
1113
 
                      g_value_take_object (value, pixbuf);
1114
 
                      retval = TRUE;
1115
 
                    }
1116
 
 
1117
 
                  g_free (path);
1118
 
                }
1119
 
            }
1120
 
          else
1121
 
            {
1122
 
              if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING))
1123
 
                {
1124
 
                  const gchar *str = g_value_get_string (&node_value);
1125
 
                  GObject *object = clutter_script_get_object (script, str);
1126
 
                  if (object)
1127
 
                    {
1128
 
                      g_value_set_object (value, object);
1129
 
                      retval = TRUE;
1130
 
                    }
1131
 
                }
1132
 
            }
1133
 
          break;
1134
 
#endif
1135
 
 
1136
 
        default:
1137
 
          retval = FALSE;
1138
 
          break;
1139
 
        }
1140
 
 
1141
 
      g_value_unset (&node_value);
1142
 
      break;
1143
 
    }
1144
 
 
1145
 
  return retval;
1146
 
}
1147
 
 
1148
 
static GList *
1149
 
clutter_script_translate_parameters (ClutterScript  *script,
1150
 
                                     GObject        *object,
1151
 
                                     const gchar    *name,
1152
 
                                     GList          *properties,
1153
 
                                     GArray        **params)
1154
 
{
1155
 
  ClutterScriptable *scriptable = NULL;
1156
 
  ClutterScriptableIface *iface = NULL;
1157
 
  GList *l, *unparsed;
1158
 
  gboolean parse_custom = FALSE;
1159
 
 
1160
 
  *params = g_array_new (FALSE, FALSE, sizeof (GParameter));
1161
 
 
1162
 
  if (CLUTTER_IS_SCRIPTABLE (object))
1163
 
    {
1164
 
      scriptable = CLUTTER_SCRIPTABLE (object);
1165
 
      iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
1166
 
 
1167
 
      if (iface->parse_custom_node)
1168
 
        parse_custom = TRUE;
1169
 
    }
1170
 
 
1171
 
  unparsed = NULL;
1172
 
 
1173
 
  for (l = properties; l != NULL; l = l->next)
1174
 
    {
1175
 
      PropertyInfo *pinfo = l->data;
1176
 
      GParameter param = { NULL };
1177
 
      gboolean res = FALSE;
1178
 
 
1179
 
      CLUTTER_NOTE (SCRIPT, "Parsing %s property (id:%s)",
1180
 
                    pinfo->pspec ? "regular" : "custom",
1181
 
                    pinfo->name);
1182
 
 
1183
 
      if (parse_custom)
1184
 
        res = iface->parse_custom_node (scriptable, script, &param.value,
1185
 
                                        pinfo->name,
1186
 
                                        pinfo->node);
1187
 
 
1188
 
      if (!res)
1189
 
        res = clutter_script_parse_node (script, &param.value,
1190
 
                                         pinfo->name,
1191
 
                                         pinfo->node,
1192
 
                                         pinfo->pspec);
1193
 
 
1194
 
      if (!res)
1195
 
        {
1196
 
          unparsed = g_list_prepend (unparsed, pinfo);
1197
 
          continue;
1198
 
        }
1199
 
 
1200
 
      param.name = g_strdup (pinfo->name);
1201
 
      
1202
 
      g_array_append_val (*params, param);
1203
 
 
1204
 
      property_info_free (pinfo);
1205
 
    }
1206
 
 
1207
 
  g_list_free (properties);
1208
 
 
1209
 
  return unparsed;
1210
 
}
1211
 
 
1212
 
static GList *
1213
 
clutter_script_construct_parameters (ClutterScript  *script,
1214
 
                                     GType           gtype,
1215
 
                                     const gchar    *name,
1216
 
                                     GList          *properties,
1217
 
                                     GArray        **construct_params)
1218
 
{
1219
 
  GObjectClass *klass;
1220
 
  GList *l, *unparsed;
1221
 
 
1222
 
  klass = g_type_class_ref (gtype);
1223
 
  g_assert (klass != NULL);
1224
 
 
1225
 
  *construct_params = g_array_new (FALSE, FALSE, sizeof (GParameter));
1226
 
 
1227
 
  unparsed = NULL;
1228
 
 
1229
 
  for (l = properties; l != NULL; l = l->next)
1230
 
    {
1231
 
      PropertyInfo *pinfo = l->data;
1232
 
      GParameter param = { NULL };
1233
 
      GParamSpec *pspec = NULL;
1234
 
 
1235
 
      /* we allow custom property names for classes, so if we
1236
 
       * don't find a corresponding GObject property for this
1237
 
       * class we just skip it and let the class itself deal
1238
 
       * with it later on
1239
 
       */
1240
 
      pspec = g_object_class_find_property (klass, pinfo->name);
1241
 
      if (pspec)
1242
 
        pinfo->pspec = g_param_spec_ref (pspec);
1243
 
      else
1244
 
        {
1245
 
          pinfo->pspec = NULL;
1246
 
          unparsed = g_list_prepend (unparsed, pinfo);
1247
 
          continue;
1248
 
        }
1249
 
 
1250
 
      if (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY))
1251
 
        {
1252
 
          unparsed = g_list_prepend (unparsed, pinfo);
1253
 
          continue;
1254
 
        }
1255
 
 
1256
 
      param.name = g_strdup (pinfo->name);
1257
 
      
1258
 
      if (!clutter_script_parse_node (script, &param.value,
1259
 
                                      pinfo->name,
1260
 
                                      pinfo->node,
1261
 
                                      pinfo->pspec))
1262
 
        {
1263
 
          unparsed = g_list_prepend (unparsed, pinfo);
1264
 
          continue;
1265
 
        }
1266
 
 
1267
 
      g_array_append_val (*construct_params, param);
1268
 
 
1269
 
      property_info_free (pinfo);
1270
 
    }
1271
 
 
1272
 
  g_list_free (properties);
1273
 
 
1274
 
  g_type_class_unref (klass);
1275
 
 
1276
 
  return unparsed;
1277
 
}
1278
 
 
1279
 
static void
1280
 
apply_behaviours (ClutterScript *script,
1281
 
                  ClutterActor  *actor,
1282
 
                  ObjectInfo    *oinfo)
1283
 
{
1284
 
  GObject *object;
1285
 
  GList *l, *unresolved;
1286
 
 
1287
 
  unresolved = NULL;
1288
 
  for (l = oinfo->behaviours; l != NULL; l = l->next)
1289
 
    {
1290
 
      const gchar *name = l->data;
1291
 
 
1292
 
      object = clutter_script_get_object (script, name);
1293
 
      if (!object)
1294
 
        {
1295
 
          ObjectInfo *behaviour_info;
1296
 
 
1297
 
          behaviour_info = g_hash_table_lookup (script->priv->objects, name);
1298
 
          if (behaviour_info)
1299
 
            object = clutter_script_construct_object (script, behaviour_info);
1300
 
        }
1301
 
 
1302
 
      if (!object)
1303
 
        {
1304
 
          unresolved = g_list_prepend (unresolved, g_strdup (name));
1305
 
          continue;
1306
 
        }
1307
 
 
1308
 
      CLUTTER_NOTE (SCRIPT, "Applying behaviour '%s' to actor of type '%s'",
1309
 
                    name,
1310
 
                    g_type_name (G_OBJECT_TYPE (actor)));
1311
 
 
1312
 
      clutter_behaviour_apply (CLUTTER_BEHAVIOUR (object), actor);
1313
 
    }
1314
 
 
1315
 
  g_list_foreach (oinfo->behaviours, (GFunc) g_free, NULL);
1316
 
  g_list_free (oinfo->behaviours);
1317
 
 
1318
 
  oinfo->behaviours = unresolved;
1319
 
}
1320
 
 
1321
 
static void
1322
 
add_children (ClutterScript    *script,
1323
 
              ClutterContainer *container,
1324
 
              ObjectInfo       *oinfo)
1325
 
{
1326
 
  GObject *object;
1327
 
  GList *l, *unresolved;
1328
 
 
1329
 
  unresolved = NULL;
1330
 
  for (l = oinfo->children; l != NULL; l = l->next)
1331
 
    {
1332
 
      const gchar *name = l->data;
1333
 
 
1334
 
      object = clutter_script_get_object (script, name);
1335
 
      if (!object)
1336
 
        {
1337
 
          ObjectInfo *child_info;
1338
 
 
1339
 
          child_info = g_hash_table_lookup (script->priv->objects, name);
1340
 
          if (child_info)
1341
 
            object = clutter_script_construct_object (script, child_info);
1342
 
        }
1343
 
 
1344
 
      if (!object)
1345
 
        {
1346
 
          unresolved = g_list_prepend (unresolved, g_strdup (name));
1347
 
          continue;
1348
 
        }
1349
 
 
1350
 
      CLUTTER_NOTE (SCRIPT, "Adding children '%s' to actor of type '%s'",
1351
 
                    name,
1352
 
                    g_type_name (G_OBJECT_TYPE (container)));
1353
 
 
1354
 
      clutter_container_add_actor (container, CLUTTER_ACTOR (object));
1355
 
    }
1356
 
 
1357
 
  g_list_foreach (oinfo->children, (GFunc) g_free, NULL);
1358
 
  g_list_free (oinfo->children);
1359
 
 
1360
 
  oinfo->children = unresolved;
1361
 
}
1362
 
 
1363
 
/* top-level classes: these classes are the roots of the
1364
 
 * hiearchy; some of them must be unreferenced, whilst
1365
 
 * others are owned by other instances
1366
 
 */
1367
 
static const struct
1368
 
{
1369
 
  const gchar *type_name;
1370
 
  guint is_toplevel : 1;
1371
 
} clutter_toplevels[] = {
1372
 
  { "ClutterActor",          FALSE },
1373
 
  { "ClutterAlpha",          FALSE },
1374
 
  { "ClutterBehaviour",      TRUE  },
1375
 
  { "ClutterEffectTemplate", TRUE  },
1376
 
  { "ClutterModel",          TRUE  },
1377
 
  { "ClutterScore",          TRUE  },
1378
 
  { "ClutterTimeline",       TRUE  }
1379
 
};
1380
 
 
1381
 
static guint n_clutter_toplevels = G_N_ELEMENTS (clutter_toplevels);
1382
 
 
1383
 
GObject *
1384
 
clutter_script_construct_object (ClutterScript *script,
1385
 
                                 ObjectInfo    *oinfo)
1386
 
{
1387
 
  GObject *object;
1388
 
  guint i;
1389
 
  GArray *params;
1390
 
  GArray *construct_params;
1391
 
  ClutterScriptable *scriptable = NULL;
1392
 
  ClutterScriptableIface *iface = NULL;
1393
 
  gboolean set_custom_property = FALSE;
1394
 
 
1395
 
  g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL);
1396
 
  g_return_val_if_fail (oinfo != NULL, NULL);
1397
 
 
1398
 
  /* we have completely updated the object */
1399
 
  if (oinfo->object && !oinfo->has_unresolved)
1400
 
    return oinfo->object;
1401
 
 
1402
 
  if (oinfo->gtype == G_TYPE_INVALID)
1403
 
    {
1404
 
      if (G_UNLIKELY (oinfo->type_func))
1405
 
        oinfo->gtype = clutter_script_get_type_from_symbol (oinfo->type_func);
1406
 
      else
1407
 
        oinfo->gtype = clutter_script_get_type_from_name (script, oinfo->class_name);
1408
 
    }
1409
 
 
1410
 
  if (G_UNLIKELY (oinfo->gtype == G_TYPE_INVALID))
1411
 
    return NULL;
1412
 
 
1413
 
  if (oinfo->object)
1414
 
    object = oinfo->object;
1415
 
  else if (oinfo->gtype == CLUTTER_TYPE_STAGE && oinfo->is_stage_default)
1416
 
    {
1417
 
      /* the default stage is a complex beast: we cannot create it using
1418
 
       * g_object_newv() but we need clutter_script_construct_parameters()
1419
 
       * to add the GParamSpec to the PropertyInfo pspec member, so
1420
 
       * that we don't have to implement every complex property (like
1421
 
       * the "color" one) directly inside the ClutterStage class.
1422
 
       */
1423
 
      oinfo->properties =
1424
 
        clutter_script_construct_parameters (script,
1425
 
                                             oinfo->gtype,
1426
 
                                             oinfo->id,
1427
 
                                             oinfo->properties,
1428
 
                                             &construct_params);
1429
 
 
1430
 
      object = G_OBJECT (clutter_stage_get_default ());
1431
 
 
1432
 
      for (i = 0; i < construct_params->len; i++)
1433
 
        {
1434
 
          GParameter *param = &g_array_index (construct_params, GParameter, i);
1435
 
 
1436
 
          g_free ((gchar *) param->name);
1437
 
          g_value_unset (&param->value);
1438
 
        }
1439
 
 
1440
 
      g_array_free (construct_params, TRUE);
1441
 
    }
1442
 
  else
1443
 
    {
1444
 
      /* every other object: first, we get the construction parameters */
1445
 
      oinfo->properties =
1446
 
        clutter_script_construct_parameters (script,
1447
 
                                             oinfo->gtype,
1448
 
                                             oinfo->id,
1449
 
                                             oinfo->properties,
1450
 
                                             &construct_params);
1451
 
 
1452
 
      object = g_object_newv (oinfo->gtype,
1453
 
                              construct_params->len,
1454
 
                              (GParameter *) construct_params->data);
1455
 
 
1456
 
      for (i = 0; i < construct_params->len; i++)
1457
 
        {
1458
 
          GParameter *param = &g_array_index (construct_params, GParameter, i);
1459
 
      
1460
 
          g_free ((gchar *) param->name);
1461
 
          g_value_unset (&param->value);
1462
 
        }
1463
 
 
1464
 
      g_array_free (construct_params, TRUE);
1465
 
   }
1466
 
 
1467
 
  /* shortcut, to avoid typechecking every time */
1468
 
  if (CLUTTER_IS_SCRIPTABLE (object))
1469
 
    {
1470
 
      scriptable = CLUTTER_SCRIPTABLE (object);
1471
 
      iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
1472
 
 
1473
 
      if (iface->set_custom_property)
1474
 
        set_custom_property = TRUE;
1475
 
    }
1476
 
 
1477
 
  /* then we get the rest of the parameters, asking the object itself
1478
 
   * to translate them for us, if we cannot do that
1479
 
   */
1480
 
  oinfo->properties = clutter_script_translate_parameters (script,
1481
 
                                                           object,
1482
 
                                                           oinfo->id,
1483
 
                                                           oinfo->properties,
1484
 
                                                           &params);
1485
 
 
1486
 
  /* consume all the properties we could translate in this pass */
1487
 
  for (i = 0; i < params->len; i++)
1488
 
    {
1489
 
      GParameter *param = &g_array_index (params, GParameter, i);
1490
 
 
1491
 
      CLUTTER_NOTE (SCRIPT,
1492
 
                    "Setting %s property '%s' (type:%s) to object '%s' (id:%s)",
1493
 
                    set_custom_property ? "custom" : "regular",
1494
 
                    param->name,
1495
 
                    g_type_name (G_VALUE_TYPE (&param->value)),
1496
 
                    g_type_name (oinfo->gtype),
1497
 
                    oinfo->id);
1498
 
 
1499
 
      if (set_custom_property)
1500
 
        iface->set_custom_property (scriptable, script,
1501
 
                                    param->name,
1502
 
                                    &param->value);
1503
 
      else
1504
 
        g_object_set_property (object, param->name, &param->value);
1505
 
 
1506
 
      g_free ((gchar *) param->name);
1507
 
      g_value_unset (&param->value);
1508
 
    }
1509
 
  g_array_free (params, TRUE);
1510
 
 
1511
 
  for (i = 0; i < n_clutter_toplevels; i++)
1512
 
    {
1513
 
      const gchar *t_name = clutter_toplevels[i].type_name;
1514
 
      GType t_type;
1515
 
      
1516
 
      t_type = clutter_script_get_type_from_name (script, t_name);
1517
 
      if (g_type_is_a (oinfo->gtype, t_type))
1518
 
        {
1519
 
          oinfo->is_toplevel = clutter_toplevels[i].is_toplevel;
1520
 
          break;
1521
 
        }
1522
 
    }
1523
 
 
1524
 
  /* XXX - at the moment, we are adding the children (and constructing
1525
 
   * the scenegraph) after we applied all the properties of an object;
1526
 
   * this usually ensures that an object is fully constructed before
1527
 
   * it is added to its parent. unfortunately, this also means that
1528
 
   * children cannot reference the parent's state inside their own
1529
 
   * definition.
1530
 
   *
1531
 
   * see bug:
1532
 
   *   http://bugzilla.openedhand.com/show_bug.cgi?id=1042
1533
 
   */
1534
 
 
1535
 
  if (oinfo->children && CLUTTER_IS_CONTAINER (object))
1536
 
    add_children (script, CLUTTER_CONTAINER (object), oinfo);
1537
 
 
1538
 
  if (oinfo->behaviours && CLUTTER_IS_ACTOR (object))
1539
 
    apply_behaviours (script, CLUTTER_ACTOR (object), oinfo);
1540
 
 
1541
 
  if (oinfo->properties || oinfo->children || oinfo->behaviours)
1542
 
    oinfo->has_unresolved = TRUE;
1543
 
  else
1544
 
    oinfo->has_unresolved = FALSE;
1545
 
 
1546
 
  if (scriptable)
1547
 
    clutter_scriptable_set_id (scriptable, oinfo->id);
1548
 
  else
1549
 
    g_object_set_data_full (object, "clutter-script-id",
1550
 
                            g_strdup (oinfo->id),
1551
 
                            g_free);
1552
 
 
1553
 
  return object;
1554
 
}
1555
 
 
1556
 
static void
1557
 
construct_each_object (gpointer key,
1558
 
                       gpointer value,
1559
 
                       gpointer data)
1560
 
{
1561
 
  ClutterScript *script = data;
1562
 
  ObjectInfo *oinfo = value;
1563
 
 
1564
 
  if (!oinfo->object)
1565
 
    oinfo->object = clutter_script_construct_object (script, oinfo);
1566
 
}
1567
 
 
1568
 
static void
1569
 
json_parse_end (JsonParser *parser,
1570
 
                gpointer    user_data)
1571
 
{
1572
 
  ClutterScript *script = user_data;
1573
 
  ClutterScriptPrivate *priv = script->priv;
1574
 
 
1575
 
  g_hash_table_foreach (priv->objects, construct_each_object, script);
1576
 
}
1577
 
 
1578
243
static GType
1579
244
clutter_script_real_get_type_from_name (ClutterScript *script,
1580
245
                                        const gchar   *type_name)
1595
260
    {
1596
261
      PropertyInfo *pinfo = data;
1597
262
 
 
263
      if (pinfo->node)
 
264
        json_node_free (pinfo->node);
 
265
 
1598
266
      if (pinfo->pspec)
1599
267
        g_param_spec_unref (pinfo->pspec);
1600
268
 
1713
381
clutter_script_class_init (ClutterScriptClass *klass)
1714
382
{
1715
383
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
384
  GParamSpec *pspec;
1716
385
 
1717
386
  g_type_class_add_private (klass, sizeof (ClutterScriptPrivate));
1718
387
 
1724
393
  /**
1725
394
   * ClutterScript:filename-set:
1726
395
   *
1727
 
   * Whether the ClutterScript:filename property is set. If this property
 
396
   * Whether the #ClutterScript:filename property is set. If this property
1728
397
   * is %TRUE then the currently parsed data comes from a file, and the
1729
 
   * file name is stored inside the ClutterScript:filename property.
 
398
   * file name is stored inside the #ClutterScript:filename property.
1730
399
   *
1731
400
   * Since: 0.6
1732
401
   */
1733
 
  g_object_class_install_property (gobject_class,
1734
 
                                   PROP_FILENAME_SET,
1735
 
                                   g_param_spec_boolean ("filename-set",
1736
 
                                                         "Filename Set",
1737
 
                                                         "Whether the :filename property is set",
1738
 
                                                         FALSE,
1739
 
                                                         CLUTTER_PARAM_READABLE));
 
402
  pspec = g_param_spec_boolean ("filename-set",
 
403
                                "Filename Set",
 
404
                                "Whether the :filename property is set",
 
405
                                FALSE,
 
406
                                CLUTTER_PARAM_READABLE);
 
407
  g_object_class_install_property (gobject_class, PROP_FILENAME_SET, pspec);
 
408
 
1740
409
  /**
1741
410
   * ClutterScript:filename:
1742
411
   *
1743
 
   * The path of the currently parsed file. If ClutterScript:filename-set
 
412
   * The path of the currently parsed file. If #ClutterScript:filename-set
1744
413
   * is %FALSE then the value of this property is undefined.
1745
414
   *
1746
415
   * Since: 0.6
1747
416
   */
1748
 
  g_object_class_install_property (gobject_class,
1749
 
                                   PROP_FILENAME,
1750
 
                                   g_param_spec_string ("filename",
1751
 
                                                        "Filename",
1752
 
                                                        "The path of the currently parsed file",
1753
 
                                                        NULL,
1754
 
                                                        CLUTTER_PARAM_READABLE));
 
417
  pspec = g_param_spec_string ("filename",
 
418
                               "Filename",
 
419
                               "The path of the currently parsed file",
 
420
                               NULL,
 
421
                               CLUTTER_PARAM_READABLE);
 
422
  g_object_class_install_property (gobject_class, PROP_FILENAME, pspec);
1755
423
}
1756
424
 
1757
425
static void
1761
429
 
1762
430
  script->priv = priv = CLUTTER_SCRIPT_GET_PRIVATE (script);
1763
431
 
1764
 
  priv->parser = json_parser_new ();
1765
 
  g_signal_connect (priv->parser,
1766
 
                    "object-end", G_CALLBACK (json_object_end),
1767
 
                    script);
1768
 
  g_signal_connect (priv->parser,
1769
 
                    "parse-end", G_CALLBACK (json_parse_end),
1770
 
                    script);
 
432
  priv->parser = g_object_new (CLUTTER_TYPE_SCRIPT_PARSER, NULL);
 
433
  priv->parser->script = script;
1771
434
 
1772
435
  priv->is_filename = FALSE;
1773
436
  priv->last_merge_id = 0;
1831
494
  priv->last_merge_id += 1;
1832
495
 
1833
496
  internal_error = NULL;
1834
 
  json_parser_load_from_file (priv->parser, filename, &internal_error);
 
497
  json_parser_load_from_file (JSON_PARSER (priv->parser),
 
498
                              filename,
 
499
                              &internal_error);
1835
500
  if (internal_error)
1836
501
    {
1837
502
      g_propagate_error (error, internal_error);
1882
547
  priv->last_merge_id += 1;
1883
548
 
1884
549
  internal_error = NULL;
1885
 
  json_parser_load_from_data (priv->parser, data, length, &internal_error);
 
550
  json_parser_load_from_data (JSON_PARSER (priv->parser),
 
551
                              data, length,
 
552
                              &internal_error);
1886
553
  if (internal_error)
1887
554
    {
1888
555
      g_propagate_error (error, internal_error);
1919
586
  if (!oinfo)
1920
587
    return NULL;
1921
588
 
1922
 
  return clutter_script_construct_object (script, oinfo);
 
589
  _clutter_script_construct_object (script, oinfo);
 
590
  _clutter_script_apply_properties (script, oinfo);
 
591
 
 
592
  return oinfo->object;
1923
593
}
1924
594
 
1925
595
static gint
2057
727
  clutter_script_ensure_objects (script);
2058
728
}
2059
729
 
 
730
static void
 
731
construct_each_objects (gpointer key,
 
732
                        gpointer value,
 
733
                        gpointer user_data)
 
734
{
 
735
  ClutterScript *script = user_data;
 
736
  ObjectInfo *oinfo = value;
 
737
 
 
738
  /* we have unfinished business */
 
739
  if (oinfo->has_unresolved)
 
740
    {
 
741
      /* this should not happen, but resilence is
 
742
       * a good thing in a parser
 
743
       */
 
744
      if (oinfo->object == NULL)
 
745
        _clutter_script_construct_object (script, oinfo);
 
746
 
 
747
      /* this will take care of setting up properties,
 
748
       * adding children and applying behaviours
 
749
       */
 
750
      _clutter_script_apply_properties (script, oinfo);
 
751
    }
 
752
}
 
753
 
2060
754
/**
2061
755
 * clutter_script_ensure_objects:
2062
756
 * @script: a #ClutterScript
2074
768
  g_return_if_fail (CLUTTER_IS_SCRIPT (script));
2075
769
 
2076
770
  priv = script->priv;
2077
 
  g_hash_table_foreach (priv->objects, construct_each_object, script);  
 
771
  g_hash_table_foreach (priv->objects, construct_each_objects, script);
2078
772
}
2079
773
 
2080
774
/**
2212
906
    }
2213
907
 
2214
908
  cd = g_new (ConnectData, 1);
2215
 
  cd->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
 
909
  cd->module = g_module_open (NULL, 0);
2216
910
  cd->data = user_data;
2217
911
 
2218
912
  clutter_script_connect_signals_full (script,
2241
935
  GObject *object = oinfo->object;
2242
936
  GList *unresolved, *l;
2243
937
 
2244
 
  if (G_UNLIKELY (!oinfo->object))
2245
 
    oinfo->object = clutter_script_construct_object (script, oinfo);
 
938
  _clutter_script_construct_object (script, oinfo);
2246
939
 
2247
940
  unresolved = NULL;
2248
941
  for (l = oinfo->signals; l != NULL; l = l->next)
2483
1176
 
2484
1177
  return retval;
2485
1178
}
 
1179
 
 
1180
/*
 
1181
 * _clutter_script_generate_fake_id:
 
1182
 * @script: a #ClutterScript
 
1183
 *
 
1184
 * Generates a fake id string for object definitions without
 
1185
 * an "id" member
 
1186
 *
 
1187
 * Return value: a newly-allocated string containing the fake
 
1188
 *   id. Use g_free() to free the resources allocated by the
 
1189
 *   returned value
 
1190
 *
 
1191
 */
 
1192
gchar *
 
1193
_clutter_script_generate_fake_id (ClutterScript *script)
 
1194
{
 
1195
  ClutterScriptPrivate *priv = script->priv;
 
1196
 
 
1197
  return g_strdup_printf ("script-%d-%d",
 
1198
                          priv->last_merge_id,
 
1199
                          priv->last_unknown++);
 
1200
}
 
1201
 
 
1202
/*
 
1203
 * _clutter_script_warn_missing_attribute:
 
1204
 * @script: a #ClutterScript
 
1205
 * @id: the id of an object definition, or %NULL
 
1206
 * @attribute: the expected attribute
 
1207
 *
 
1208
 * Emits a warning, using GLib's log facilities, for a missing
 
1209
 * @attribute in an object definition, pointing to the current
 
1210
 * location of the #ClutterScriptParser
 
1211
 */
 
1212
void
 
1213
_clutter_script_warn_missing_attribute (ClutterScript *script,
 
1214
                                        const gchar   *id,
 
1215
                                        const gchar   *attribute)
 
1216
{
 
1217
  ClutterScriptPrivate *priv = script->priv;
 
1218
  JsonParser *parser = JSON_PARSER (priv->parser);
 
1219
  gint current_line = json_parser_get_current_line (parser);
 
1220
 
 
1221
  if (id != NULL && *id != '\0')
 
1222
    {
 
1223
      g_warning ("%s:%d: object '%s' has no '%s' attribute",
 
1224
                 priv->is_filename ? priv->filename : "<input>",
 
1225
                 current_line,
 
1226
                 id,
 
1227
                 attribute);
 
1228
    }
 
1229
  else
 
1230
    {
 
1231
      g_warning ("%s:%d: object has no '%s' attribute",
 
1232
                 priv->is_filename ? priv->filename : "<input>",
 
1233
                 current_line,
 
1234
                 attribute);
 
1235
    }
 
1236
}
 
1237
 
 
1238
/*
 
1239
 * _clutter_script_warn_invalid_value:
 
1240
 * @script: a #ClutterScript
 
1241
 * @attribute: the attribute with the invalid value
 
1242
 * @expected: a string with the expected value
 
1243
 * @node: a #JsonNode containing the value
 
1244
 *
 
1245
 * Emits a warning, using GLib's log facilities, for an invalid
 
1246
 * value found when parsing @attribute, pointing to the current
 
1247
 * location of the #ClutterScriptParser
 
1248
 */
 
1249
void
 
1250
_clutter_script_warn_invalid_value (ClutterScript *script,
 
1251
                                    const gchar   *attribute,
 
1252
                                    const gchar   *expected,
 
1253
                                    JsonNode      *node)
 
1254
{
 
1255
  ClutterScriptPrivate *priv = script->priv;
 
1256
  JsonParser *parser = JSON_PARSER (priv->parser);
 
1257
  gint current_line = json_parser_get_current_line (parser);
 
1258
 
 
1259
  if (node != NULL)
 
1260
    {
 
1261
      g_warning ("%s:%d: invalid value of type '%s' for attribute '%s':"
 
1262
                 "a value of type '%s' is expected",
 
1263
                 priv->is_filename ? priv->filename : "<input>",
 
1264
                 current_line,
 
1265
                 json_node_type_name (node),
 
1266
                 attribute,
 
1267
                 expected);
 
1268
    }
 
1269
  else
 
1270
    {
 
1271
      g_warning ("%s:%d: invalid value for attribute '%s':"
 
1272
                 "a value of type '%s' is expected",
 
1273
                 priv->is_filename ? priv->filename : "<input>",
 
1274
                 current_line,
 
1275
                 attribute,
 
1276
                 expected);
 
1277
    }
 
1278
}
 
1279
 
 
1280
/*
 
1281
 * _clutter_script_get_object_info:
 
1282
 * @script: a #ClutterScript
 
1283
 * @script_id: the id of the object definition
 
1284
 *
 
1285
 * Retrieves the #ObjectInfo for the given @script_id
 
1286
 *
 
1287
 * Return value: a #ObjectInfo or %NULL
 
1288
 */
 
1289
ObjectInfo *
 
1290
_clutter_script_get_object_info (ClutterScript *script,
 
1291
                                 const gchar   *script_id)
 
1292
{
 
1293
  ClutterScriptPrivate *priv = script->priv;
 
1294
 
 
1295
  return g_hash_table_lookup (priv->objects, script_id);
 
1296
}
 
1297
 
 
1298
/*
 
1299
 * _clutter_script_get_last_merge_id:
 
1300
 * @script: a #ClutterScript
 
1301
 *
 
1302
 * Retrieves the last merge id of @script. The merge id
 
1303
 * should be stored inside an #ObjectInfo. If you need
 
1304
 * a unique fake id for object definitions with an "id"
 
1305
 * member, consider using _clutter_script_generate_fake_id()
 
1306
 * instead
 
1307
 *
 
1308
 * Return value: the last merge id
 
1309
 */
 
1310
guint
 
1311
_clutter_script_get_last_merge_id (ClutterScript *script)
 
1312
{
 
1313
  return script->priv->last_merge_id;
 
1314
}
 
1315
 
 
1316
/*
 
1317
 * _clutter_script_add_object_info:
 
1318
 * @script: a #ClutterScript
 
1319
 * @oinfo: a #ObjectInfo
 
1320
 *
 
1321
 * Adds @oinfo inside the objects list held by @script
 
1322
 */
 
1323
void
 
1324
_clutter_script_add_object_info (ClutterScript *script,
 
1325
                                 ObjectInfo    *oinfo)
 
1326
{
 
1327
  ClutterScriptPrivate *priv = script->priv;
 
1328
 
 
1329
  g_hash_table_steal (priv->objects, oinfo->id);
 
1330
  g_hash_table_insert (priv->objects, oinfo->id, oinfo);
 
1331
}