Add LaughActor
[laugh.git] / src / laugh-dom.c
blobb1339bd03f4193b2c85b47b9d1c0438df01de010
1 /*
2 * Laugh.
4 * An glib SMIL library.
6 * Authored By Koos Vriezen <koos.vriezen@gmail.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 /**
25 * SECTION:laugh-io
26 * @short_description: DOM bases classes.
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
33 #include "laugh-dom.h"
34 #include "laugh-io.h"
35 #include "laugh-layout.h"
36 #include "laugh-timing.h"
37 #include "laugh-animate.h"
38 #include "laugh-media.h"
39 #include "laugh-linking.h"
41 #include <string.h>
42 #include <expat.h>
43 #include <glib/gprintf.h>
45 #define LAUGH_NODE_GET_PRIVATE(obj) \
46 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_NODE, LaughNodePrivate))
47 #define LAUGH_DOCUMENT_GET_PRIVATE(obj) \
48 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_DOCUMENT, LaughDocumentPrivate))
50 enum
52 INITIALIZED,
53 STARTED,
54 STOPPED,
56 LAST_SIGNAL
59 typedef struct _DocumentBuilder
61 LaughNode *top_node;
62 LaughNode *current;
63 } DocumentBuilder;
65 typedef struct _LaughIdMapping
67 GHashTable *string_id;
68 GHashTable *id_string;
69 long string_unknown;
70 } LaughIdMapping;
72 static guint laugh_node_signals[LAST_SIGNAL] = { 0 };
74 static LaughIdMapping _laugh_tag_map;
75 static LaughIdMapping _laugh_attr_map;
78 struct _LaughNodePrivate
80 GHashTable *attribute_undo;
83 static gpointer laugh_node_parent_class = ((void *)0);
85 static void laugh_node_finalize (GObject *object)
87 G_OBJECT_CLASS (laugh_node_parent_class)->finalize (object);
90 static void _laugh_node_attr_free (gpointer key, gpointer value, gpointer data)
92 (void) key; (void) data;
94 g_free (value);
97 static void _laugh_node_undo_free (gpointer key, gpointer value, gpointer data)
99 GSList *list;
100 (void) key; (void) data;
102 for (list = (GSList *) value; list; list = list->next)
103 g_free (list->data);
104 g_slist_free (list);
107 static void _laugh_node_children_clear (LaughNode *node)
109 LaughNode *child;
111 for (child = node->first_child; child; ) {
112 LaughNode *tmp = child->next_sibling;
113 g_object_unref (child);
114 child = tmp;
116 node->first_child = node->last_child = NULL;
119 static void laugh_node_dispose (GObject *object)
121 LaughNode *node = LAUGH_NODE(object);
122 LaughNodePrivate *priv = node->priv;
124 if (node->attributes) {
125 g_hash_table_foreach (node->attributes, _laugh_node_attr_free, NULL);
126 g_hash_table_destroy (node->attributes);
129 if (priv->attribute_undo) {
130 g_hash_table_foreach (priv->attribute_undo, _laugh_node_undo_free,NULL);
131 g_hash_table_destroy (priv->attribute_undo);
134 _laugh_node_children_clear (node);
136 G_OBJECT_CLASS (laugh_node_parent_class)->dispose (object);
139 static void _laugh_node_set_attribute (LaughNode *node,
140 LaughNodeAttributeId attr, const gchar *val, gpointer *undo)
142 if (LaughAttrIdId == attr)
143 node->element_id = laugh_document_id_from_string (node->document, val);
145 /*This is ugly, during initialize g_hash_table_foreach call us */
146 if (node->state > LaughStateInit) {
147 LaughNodePrivate *priv = node->priv;
148 gchar *cur = NULL;
149 gchar *new_cur = NULL;
151 if (node->attributes)
152 cur = g_hash_table_lookup (node->attributes, (gpointer)(long) attr);
153 else
154 node->attributes = g_hash_table_new (NULL, NULL);
156 if (cur == val)
157 return;
159 if (val) {
160 new_cur = g_strdup (val);
161 g_hash_table_replace (node->attributes,
162 (gpointer) (long) attr, new_cur);
163 } else {
164 g_hash_table_remove (node->attributes, (gpointer) (long) attr);
167 if (undo) {
169 if (val) { /*set*/
170 *(const gchar **)undo = new_cur;
171 g_printf ("set undo to %s\n", new_cur);
173 if (cur) {
174 GSList *ol = NULL;
176 if (priv->attribute_undo)
177 ol = (GSList *) g_hash_table_lookup (
178 priv->attribute_undo, (gpointer) (long) attr);
179 else
180 priv->attribute_undo = g_hash_table_new (NULL, NULL);
182 ol = g_slist_prepend (ol, cur);
183 g_hash_table_replace (priv->attribute_undo,
184 (gpointer) (long) attr, ol);
186 } else { /*unset*/
187 const gchar *undo_val = *(gchar **)undo;
189 if (undo_val && priv->attribute_undo) {
190 GSList *ol = ol = (GSList *) g_hash_table_lookup (
191 priv->attribute_undo, (gpointer) (long) attr);
193 if (ol) {
194 if (undo_val == cur) {
195 new_cur = ol->data;
196 g_hash_table_replace (node->attributes,
197 (gpointer) (long) attr, new_cur);
198 ol = ol->next;
199 } else {
200 ol = g_slist_remove (ol, undo_val);
201 g_free (undo_val);
204 if (!ol)
205 g_hash_table_remove (priv->attribute_undo,
206 (gpointer) (long) attr);
207 else
208 g_hash_table_replace (priv->attribute_undo,
209 (gpointer) (long) attr, ol);
213 if (cur)
214 g_free (cur);
216 g_printf ("set undo to %s\n", new_cur ? new_cur : "-");
217 *(gchar **)undo = new_cur;
219 } else if (cur) {
220 g_free (cur);
225 static const gchar *_laugh_node_get_attribute (LaughNode *node,
226 LaughNodeAttributeId att)
228 if (node->attributes)
229 return (gchar *) g_hash_table_lookup (
230 node->attributes, (gpointer) (long) att);
231 return NULL;
234 static void _laugh_node_init (LaughNode *node, LaughInitializer *initializer)
236 LaughNode *child;
238 if (node->attributes)
239 g_hash_table_foreach (node->attributes, laugh_attributes_set, node);
241 for (child = node->first_child; child; child = child->next_sibling)
242 laugh_node_init (child, initializer);
245 static void _laugh_node_start (LaughNode *node)
247 node->state = LaughStateBegun;
248 g_signal_emit (node, laugh_node_signals[STARTED], 0);
251 static void _laugh_node_stop (LaughNode *node)
253 node->state = LaughStateStopped;
254 g_signal_emit (node, laugh_node_signals[STOPPED], 0);
257 static void _laugh_node_freeze (LaughNode *node)
259 node->state = LaughStateFreezed;
262 static void _laugh_add_mapping (LaughIdMapping *m, long id, const gchar *s)
264 g_hash_table_insert (m->string_id, (gpointer) s, (gpointer) id);
265 g_hash_table_insert (m->id_string, (gpointer) id, (gpointer) s);
268 static void laugh_node_class_init (LaughNodeClass *klass)
270 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
272 laugh_node_parent_class = g_type_class_peek_parent (klass);
274 gobject_class->finalize = laugh_node_finalize;
275 gobject_class->dispose = laugh_node_dispose;
276 klass->set_attribute = _laugh_node_set_attribute;
277 klass->get_attribute = _laugh_node_get_attribute;
278 klass->init = _laugh_node_init;
279 klass->freeze = _laugh_node_freeze;
281 g_type_class_add_private (gobject_class, sizeof (LaughNodePrivate));
283 laugh_node_signals[INITIALIZED] =
284 g_signal_new ("initialized",
285 G_TYPE_FROM_CLASS (gobject_class),
286 G_SIGNAL_RUN_LAST,
287 G_STRUCT_OFFSET (LaughNodeClass, initialized),
288 NULL, NULL,
289 g_cclosure_marshal_VOID__VOID,
290 G_TYPE_NONE, 0);
292 laugh_node_signals[STARTED] =
293 g_signal_new ("started",
294 G_TYPE_FROM_CLASS (gobject_class),
295 G_SIGNAL_RUN_LAST,
296 G_STRUCT_OFFSET (LaughNodeClass, started),
297 NULL, NULL,
298 g_cclosure_marshal_VOID__VOID,
299 G_TYPE_NONE, 0);
301 laugh_node_signals[STOPPED] =
302 g_signal_new ("stopped",
303 G_TYPE_FROM_CLASS (gobject_class),
304 G_SIGNAL_RUN_LAST,
305 G_STRUCT_OFFSET (LaughNodeClass, stopped),
306 NULL, NULL,
307 g_cclosure_marshal_VOID__VOID,
308 G_TYPE_NONE, 0);
310 _laugh_tag_map.string_id = g_hash_table_new (g_str_hash, g_str_equal);
311 _laugh_tag_map.id_string = g_hash_table_new (NULL, NULL);
312 _laugh_tag_map.string_unknown = LaughTagIdLast;
314 _laugh_attr_map.string_id = g_hash_table_new (g_str_hash, g_str_equal);
315 _laugh_attr_map.id_string = g_hash_table_new (NULL, NULL);
316 _laugh_attr_map.string_unknown = LaughAttrIdLast;
318 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdDocument, "document");
319 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdSmil, "smil");
320 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdHead, "head");
321 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdLayout, "layout");
322 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdRootLayout, "root-layout");
323 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdRegion, "region");
324 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdBody, "body");
325 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdSeq, "seq");
326 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdPar, "par");
327 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdExcl, "excl");
328 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdSwitch, "switch");
329 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdAudio, "audio");
330 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdBrush, "brush");
331 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdImg, "img");
332 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdRef, "ref");
333 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdText, "text");
334 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdVideo, "video");
335 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdSet, "set");
336 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdAnimate, "animate");
337 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdAnimateMotion, "animateMotion");
338 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdA, "a");
339 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdAnchor, "anchor");
340 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdArea, "area");
342 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdId, "id");
343 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdName, "name");
344 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdLeft, "left");
345 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTop, "top");
346 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdWidth, "width");
347 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdHeight, "height");
348 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdRight, "right");
349 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBottom, "bottom");
350 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBgColor, "background-color");
351 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBgColor1, "backgroundColor");
352 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBgImage, "backgroundImage");
353 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdShowBg, "showBackground");
354 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdZIndex, "z-index");
355 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBegin, "begin");
356 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdDur, "dur");
357 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdEnd, "end");
358 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdEndSync, "endSync");
359 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdRepeat, "repeat");
360 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdSrc, "src");
361 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdRegion, "region");
362 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdType, "type");
363 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdSubType, "subType");
364 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFill, "fil");
365 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFit, "fit");
366 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdPeers, "peers");
367 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdHigher, "higher");
368 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdLower, "lower");
369 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdPauseDisplay, "pauseDisplay");
370 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdHRef, "href");
371 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdCoord, "coords");
372 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTransIn, "trans-in");
373 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTransOut, "trans-out");
374 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdSystemBitrate, "system-bitrate");
375 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdSensitivity, "sensitivity");
376 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFontColor, "fontColor");
377 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFontSize, "fontSize");
378 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdHAlign, "hAlign");
379 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdVAlign, "vAlign");
380 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdCharset, "charset");
381 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBgOpacity, "backgroundOpacity");
382 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTargetElement, "targetElement");
383 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTarget, "target");
384 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdAttr, "attribute");
385 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdAttrName, "attributeName");
386 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTo, "to");
387 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdChangeBy, "changed-by");
388 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFrom, "from");
389 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdValues, "values");
390 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdCalcMode, "calcMode");
391 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdKeyTimes, "keyTimes");
392 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdKeySplines, "keySplines");
395 static void laugh_node_instance_init (GTypeInstance *instance, gpointer g_class)
397 LaughNode *self = (LaughNode *)instance;
399 self->priv = LAUGH_NODE_GET_PRIVATE (self);
401 self->state = LaughStateInit;
402 self->parent_node = NULL;
403 self->first_child = NULL;
404 self->previous_sibling = NULL;
405 self->next_sibling = NULL;
406 self->attributes = NULL;
409 GType laugh_node_get_type (void)
411 static GType type = 0;
412 if (type == 0) {
413 static const GTypeInfo info = {
414 sizeof (LaughNodeClass),
415 NULL, /* base_init */
416 NULL, /* base_finalize */
417 (GClassInitFunc) laugh_node_class_init, /* class_init */
418 NULL, /* class_finalize */
419 NULL, /* class_data */
420 sizeof (LaughNode),
421 0, /* n_preallocs */
422 laugh_node_instance_init /* instance_init */
424 type = g_type_register_static (G_TYPE_OBJECT,
425 "LaughNodeType",
426 &info, 0);
428 return type;
431 struct _LaughDocumentPrivate
433 gchar *uri;
434 LaughIO *io;
435 GHashTable *element_id_table;
436 guint element_id_last;
437 ClutterContainer *parent_actor;
438 guint width;
439 guint height;
442 static gpointer laugh_document_parent_class = ((void *)0);
444 static void laugh_document_finalize (GObject *object)
446 G_OBJECT_CLASS (laugh_document_parent_class)->finalize (object);
449 static void laugh_document_dispose (GObject *object)
451 LaughDocument *document = LAUGH_DOCUMENT(object);
452 LaughRoleTiming *role = document->timing;
454 g_free (document->priv->uri);
455 if (document->priv->element_id_table)
456 g_hash_table_destroy (document->priv->element_id_table);
458 G_OBJECT_CLASS (laugh_document_parent_class)->dispose (object);
460 laugh_timing_role_delete (role);
463 static
464 void _lauch_document_mime_type (LaughIO *io, const gchar *mime, gpointer d)
466 g_printf ("lauch_document_mime_type: %s\n", mime);
467 if (strcmp (mime, "application/smil")) {
468 LaughNode *node = LAUGH_NODE (d);
470 g_printerr ("not a smil document\n");
471 laugh_io_cancel (io);
473 g_object_unref (G_OBJECT (io));
475 laugh_node_base_emit_initialized (node);
479 static void
480 _lauch_document_completed (LaughIO *io, gsize sz, gpointer data, gpointer d)
482 LaughInitializer *initializer = (LaughInitializer *)d;
483 LaughNode *node = initializer->node;
485 g_printf ("_lauch_document_completed: %u\n", sz);
486 if (data) {
487 laugh_node_set_inner_xml (node, (const gchar *) data);
489 g_free (data);
491 if (node->first_child)
492 laugh_node_init (node->first_child, initializer);
494 g_object_unref (G_OBJECT (io));
496 if (!initializer->init_pending)
497 laugh_node_base_emit_initialized (node);
500 static void _laugh_document_init (LaughNode *doc, LaughInitializer *initializer)
502 LaughDocument *document = (LaughDocument *) doc;
503 LaughDocumentPrivate *priv = document->priv;
505 initializer->node = doc;
506 initializer->parent_segment = document->timing;
507 priv->io = laugh_io_new (priv->uri, NULL);
508 priv->element_id_table = g_hash_table_new_full (g_str_hash, g_str_equal,
509 (GDestroyNotify) g_free, NULL);
510 priv->parent_actor = CLUTTER_ACTOR (initializer->parent_region);
511 g_printf( "_laugh_document_init %d %d %d %d\n",
512 priv->width, priv->height, initializer->parent_width, initializer->parent_height);
513 priv->width = initializer->parent_width;
514 priv->height = initializer->parent_height;
516 g_signal_connect (G_OBJECT (priv->io), "mime-type",
517 (GCallback) _lauch_document_mime_type, (gpointer) doc);
518 g_signal_connect (G_OBJECT (priv->io), "completed",
519 (GCallback) _lauch_document_completed, (gpointer) initializer);
521 g_free (priv->uri);
522 priv->uri = g_strdup (laugh_io_get_uri (priv->io));
524 laugh_io_open (priv->io);
527 static void _laugh_document_start (LaughNode *node)
529 LaughDocument *doc = (LaughDocument *) node;
531 if (!doc->timing->active)
532 laugh_timing_setting_start (doc->timing);
535 static void _laugh_document_stop (LaughNode *node)
537 LaughDocument *doc = (LaughDocument *) node;
539 if (doc->timing->active)
540 laugh_timing_setting_stop (doc->timing);
543 static void laugh_document_class_init (LaughDocumentClass *klass)
545 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
546 LaughNodeClass *node_class = (LaughNodeClass *) klass;
548 laugh_document_parent_class = g_type_class_peek_parent (klass);
550 gobject_class->finalize = laugh_document_finalize;
551 gobject_class->dispose = laugh_document_dispose;
552 node_class->init = _laugh_document_init;
553 node_class->start = _laugh_document_start;
554 node_class->stop = _laugh_document_stop;
556 g_type_class_add_private (gobject_class, sizeof (LaughDocumentPrivate));
559 static
560 void laugh_document_instance_init (GTypeInstance *instance, gpointer g_class)
562 LaughDocument *self = (LaughDocument *)instance;
564 self->priv = LAUGH_DOCUMENT_GET_PRIVATE (self);
565 self->timing = laugh_timing_role_new ((LaughNode *) self);
568 GType laugh_document_get_type (void)
570 static GType type = 0;
571 if (type == 0) {
572 static const GTypeInfo info = {
573 sizeof (LaughDocumentClass),
574 NULL, /* base_init */
575 NULL, /* base_finalize */
576 (GClassInitFunc) laugh_document_class_init, /* class_init */
577 NULL, /* class_finalize */
578 NULL, /* class_data */
579 sizeof (LaughDocument),
580 0, /* n_preallocs */
581 laugh_document_instance_init /* instance_init */
583 type = g_type_register_static (LAUGH_TYPE_NODE,
584 "LaughDocumentType",
585 &info, 0);
587 return type;
590 static long _laugh_mapping_from_string (LaughIdMapping *map, const gchar *tag)
592 gpointer id = g_hash_table_lookup (map->string_id, (gpointer) tag);
593 if (!id) {
594 id = (gpointer) map->string_unknown++;
595 _laugh_add_mapping (map, (long) id, g_strdup (tag));
597 return (long) id;
600 LaughNodeTagId laugh_tag_from_string (const gchar *tag)
602 return (LaughNodeTagId) _laugh_mapping_from_string (&_laugh_tag_map, tag);
605 const gchar *laugh_tag_from_id (LaughNodeTagId id)
607 return (const gchar *) g_hash_table_lookup (_laugh_tag_map.id_string,
608 (gpointer) id);
611 LaughNodeAttributeId laugh_attribute_from_string (const gchar *attr)
613 return (LaughNodeAttributeId)_laugh_mapping_from_string (
614 &_laugh_attr_map, attr);
617 const gchar *laugh_attribute_from_id (LaughNodeAttributeId id)
619 return (const gchar *) g_hash_table_lookup (_laugh_attr_map.id_string,
620 (gpointer) id);
623 static void
624 _laugh_dom_initializer_initialized (LaughNode *node, LaughInitializer *init)
626 init->init_pending = g_slist_remove (init->init_pending, node);
627 if (!init->init_pending)
628 laugh_node_base_emit_initialized (init->node);
631 LaughInitializer *laugh_dom_initializer_new (ClutterContainer *parent,
632 guint width, guint height)
634 LaughInitializer *initializer = g_new0 (LaughInitializer, 1);
636 initializer->parent_region = parent;
637 initializer->parent_width = width;
638 initializer->parent_height = height;
639 initializer->initialized = _laugh_dom_initializer_initialized;
641 return initializer;
644 LaughNode *laugh_node_new (LaughDocument *document, LaughNodeTagId id,
645 GHashTable *attrs)
647 LaughNode *node = LAUGH_NODE (g_object_new (LAUGH_TYPE_NODE, NULL));
649 laugh_node_base_construct (document, node, id, attrs);
651 return node;
654 LaughDocument *laugh_document_new (const gchar *uri)
656 LaughDocument *d = LAUGH_DOCUMENT(g_object_new (LAUGH_TYPE_DOCUMENT, NULL));
657 LaughNode *node = (LaughNode *)d;
659 laugh_node_base_construct (d, node, LaughTagIdDocument, NULL);
660 d->priv->uri = g_strdup (uri);
662 return d;
665 void laugh_node_base_construct (LaughDocument *document, LaughNode *node,
666 LaughNodeTagId id, GHashTable *attributes)
668 node->id = id;
669 node->document = document;
670 node->attributes = attributes;
673 void laugh_attributes_set (gpointer key, gpointer val, gpointer data)
675 laugh_node_set_attribute ((LaughNode *)data,
676 (LaughNodeAttributeId) (long) key, (const gchar *) val, NULL);
679 void laugh_node_base_emit_initialized (LaughNode *node)
681 g_signal_emit (node, laugh_node_signals[INITIALIZED], 0);
684 const gchar *laugh_node_get_attribute (LaughNode *node,
685 LaughNodeAttributeId attribute)
687 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
689 return klass->get_attribute (node, attribute);
692 void laugh_node_set_attribute (LaughNode *node,
693 LaughNodeAttributeId attr, const gchar *value, gpointer *undo)
695 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
697 klass->set_attribute (node, attr, value, undo);
700 static
701 void _laugh_xml_start_tag (void *data, const char *tag, const char **attr)
703 DocumentBuilder *builder = (DocumentBuilder *) data;
704 LaughNode *node;
705 GHashTable *attrs = NULL;
706 LaughNodeTagId id = laugh_tag_from_string (tag);
708 if (attr && attr[0]) {
709 int i;
710 attrs = g_hash_table_new (NULL, NULL);
711 for (i = 0; attr[i]; i += 2)
712 g_hash_table_insert (attrs,
713 (gpointer) (long) laugh_attribute_from_string (attr[i]),
714 g_strdup (attr[i+1]));
716 switch (id) {
717 case LaughTagIdLayout:
718 case LaughTagIdRootLayout:
719 case LaughTagIdRegion:
720 node = laugh_layout_new (builder->top_node->document, id, attrs);
721 break;
722 case LaughTagIdBody:
723 case LaughTagIdSeq:
724 case LaughTagIdPar:
725 case LaughTagIdExcl:
726 node = laugh_timing_container_new (builder->top_node->document,
727 id, attrs);
728 break;
729 case LaughTagIdSet:
730 case LaughTagIdAnimate:
731 case LaughTagIdAnimateMotion:
732 node = laugh_animate_new (builder->top_node->document, id, attrs);
733 break;
734 case LaughTagIdAudio:
735 case LaughTagIdBrush:
736 case LaughTagIdImg:
737 case LaughTagIdRef:
738 case LaughTagIdText:
739 case LaughTagIdVideo:
740 node = laugh_media_new (builder->top_node->document, id, attrs);
741 break;
742 case LaughTagIdA:
743 case LaughTagIdAnchor:
744 case LaughTagIdArea:
745 node = laugh_linking_new (builder->top_node->document, id, attrs);
746 break;
747 default:
748 node = laugh_node_new (builder->top_node->document, id, attrs);
750 laugh_node_append_child (builder->current, node);
752 builder->current = node;
755 static void _laugh_xml_end_tag (void *data, const char *tag)
757 DocumentBuilder *builder = (DocumentBuilder *) data;
759 if (laugh_tag_from_string (tag) != builder->current->id)
760 g_printerr ("unmatched tag %s, expects %s\n",
761 tag, laugh_tag_from_id (builder->current->id));
762 else if (builder->current != builder->top_node)
763 builder->current = builder->current->parent_node;
764 else
765 g_printerr ("spurious end tag %s", tag);
768 void laugh_node_set_inner_xml (LaughNode *node, const gchar *xml)
770 DocumentBuilder builder;
771 XML_Parser parser = XML_ParserCreate (0L);
773 _laugh_node_children_clear (node);
775 builder.top_node = node;
776 builder.current = node;
778 XML_SetUserData (parser, &builder);
779 XML_SetElementHandler (parser, _laugh_xml_start_tag, _laugh_xml_end_tag);
781 if (XML_Parse (parser, xml, strlen (xml), TRUE) == XML_STATUS_ERROR) {
782 /*TODO: clear all*/
785 XML_ParserFree(parser);
788 static void _laugh_node_get_outer_xml (LaughNode *node, GString *buffer, int sp)
790 gchar *indent = "";
792 if (sp > 0) {
793 indent = (gchar *) g_malloc (sp + 1);
794 memset (indent, ' ', sp);
795 indent[sp] = 0;
796 g_string_append (buffer, indent);
799 g_string_append_c (buffer, '<');
800 g_string_append (buffer, laugh_tag_from_id (node->id));
801 if (node->attributes) {
802 GHashTableIter iter;
803 gpointer key, value;
804 g_hash_table_iter_init (&iter, node->attributes);
805 while (g_hash_table_iter_next (&iter, &key, &value)) {
806 g_string_append_c (buffer, ' ');
807 g_string_append (buffer, laugh_attribute_from_id ((long) key));
808 g_string_append (buffer, "=\"");
809 g_string_append (buffer, (const gchar *)value);/*TODO escape*/
810 g_string_append_c (buffer, '"');
813 if (node->first_child) {
814 LaughNode *child;
815 g_string_append (buffer, ">\n");
816 for (child = node->first_child; child; child = child->next_sibling)
817 _laugh_node_get_outer_xml (child, buffer, sp+2);
818 g_string_append (buffer, indent);
819 g_string_append (buffer, "</");
820 g_string_append (buffer, laugh_tag_from_id (node->id));
821 g_string_append (buffer, ">\n");
822 } else {
823 g_string_append (buffer, "/>\n");
826 if (*indent)
827 g_free (indent);
830 gchar *laugh_node_get_inner_xml (LaughNode *node)
832 GString *buffer = g_string_new ("");
833 LaughNode *child;
834 gchar *xml;
836 for (child = node->first_child; child; child = child->next_sibling)
837 _laugh_node_get_outer_xml (child, buffer, 0);
839 xml = g_string_free (buffer, FALSE);
841 return xml;
844 void laugh_node_append_child (LaughNode *node, LaughNode *child)
846 LaughNode *n = node->last_child;
848 if (n) {
849 n->next_sibling = child;
850 child->previous_sibling = n;
851 child->next_sibling = NULL;
852 } else {
853 node->first_child = child;
854 child->previous_sibling = NULL;
855 child->next_sibling = NULL;
857 node->last_child = child;
858 child->parent_node = node;
861 void laugh_node_init (LaughNode *node, LaughInitializer *initializer)
863 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
865 klass->init (node, initializer);
868 void laugh_node_start (LaughNode *node)
870 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
872 if (klass->start)
873 klass->start (node);
874 _laugh_node_start (node);
877 void laugh_node_stop (LaughNode *node)
879 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
881 if (klass->stop)
882 klass->stop (node);
883 _laugh_node_stop (node);
886 void laugh_node_freeze (LaughNode *node)
888 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
890 if (klass->freeze)
891 klass->freeze (node);
894 guint laugh_document_id_from_string (LaughDocument *doc, const gchar *str)
896 LaughDocumentPrivate *priv = doc->priv;
897 guint id = (long) g_hash_table_lookup (priv->element_id_table, str);
899 if (!id) {
900 id = ++priv->element_id_last;
901 g_hash_table_insert (priv->element_id_table,
902 g_strdup (str), (gpointer) (long) id);
905 return id;
908 LaughNode *_laugh_node_get_element_by_id (LaughNode *node, const guint id)
910 LaughNode *child;
912 if (id == node->element_id)
913 return node;
915 for (child = node->first_child; child; child = child->next_sibling) {
916 LaughNode *n = _laugh_node_get_element_by_id (child, id);
917 if (n)
918 return n;
921 return NULL;
924 LaughNode *laugh_document_get_element_by_id (LaughDocument *doc, guint id)
926 return _laugh_node_get_element_by_id ((LaughNode *) doc, id);
929 const gchar *laugh_document_get_uri (LaughDocument *doc)
931 return doc->priv->uri;
934 LaughRole *laugh_node_role_get (LaughNode *node, LaughRoleType type)
936 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
938 if (klass->role)
939 return klass->role (node, type);
941 return NULL;
944 void laugh_document_set_uri (LaughDocument *doc, const gchar *uri)
946 LaughNode *node = LAUGH_NODE (doc);
947 LaughDocumentPrivate *priv = doc->priv;
949 _laugh_node_children_clear (node);
951 if (priv->uri) {
952 gchar *new_uri = laugh_io_uri_resolve (uri, priv->uri);
953 g_free (priv->uri);
954 priv->uri = new_uri;
955 } else {
956 priv->uri = g_strdup (uri);
959 laugh_timing_setting_stop (doc->timing);
960 node->state = LaughStateInit;
963 static void _laugh_document_initialized (LaughNode *node, LaughInitializer *initializer)
965 g_signal_handler_disconnect (G_OBJECT (node), initializer->init_signal);
967 g_free (initializer);
969 laugh_node_start (node);
972 gboolean
973 laugh_document_open (LaughDocument *doc, ClutterContainer *parent, gint w, gint h)
975 /*TODO: create an actor instead of passing a parent and wxh*/
976 LaughInitializer *initializer;
977 LaughDocumentPrivate *priv = doc->priv;
979 if (!priv->uri) {
980 g_printerr ("laugh_document_open: no uri set\n");
981 return FALSE;
984 if (!parent)
985 parent = priv->parent_actor;
986 if (w < 0)
987 w = priv->width;
988 if (h < 0)
989 h = priv->height;
991 initializer = laugh_dom_initializer_new (parent, w, h);
993 initializer->init_signal = g_signal_connect (G_OBJECT (doc), "initialized",
994 (GCallback) _laugh_document_initialized, initializer);
996 laugh_node_init (LAUGH_NODE (doc), initializer);
998 clutter_actor_show_all (CLUTTER_ACTOR (parent));
1000 return TRUE;
1003 #ifdef LAUGH_DOM_TEST
1004 /* gcc laugh-dom.c laugh-io.c laugh-layout.c -o domtest `pkg-config --libs --cflags gio-2.0 glib-2.0 gthread-2.0 clutter-gtk-0.6` -lexpat -DLAUGH_DOM_TEST */
1005 #include <unistd.h>
1006 #include <sys/types.h>
1007 #include <sys/stat.h>
1008 #include <fcntl.h>
1010 static GMainLoop *mainloop;
1012 void initialized_callback (LaughNode *node, gpointer d)
1014 gchar *xml = laugh_node_get_inner_xml (node);
1015 g_printf ("cb initialized\n%s\n", xml);
1017 g_free (xml);
1019 g_main_loop_quit (mainloop);
1022 int main (int argc, char **argv)
1024 LaughDocument *doc;
1025 LaughInitializer *initializer;
1027 if (argc < 2) {
1028 g_printerr ("usage %s uri\n", argv[0]);
1029 return 1;
1032 g_thread_init (NULL);
1033 g_type_init ();
1035 doc = laugh_document_new (argv[1]);
1036 initializer = laugh_dom_initializer_new (NULL, 640, 480);
1038 g_signal_connect (G_OBJECT (doc), "initialized",
1039 (GCallback) initialized_callback, (gpointer) 0);
1041 laugh_node_init (LAUGH_NODE (doc), initializer);
1043 mainloop = g_main_loop_new (NULL, TRUE);
1044 g_main_loop_run (mainloop);
1046 g_object_unref (G_OBJECT (doc));
1047 g_free (initializer);
1049 return 0;
1052 #endif