Build the timings tree. Remove next from segments and make sub_segments GSList values
[laugh.git] / src / laugh-dom.c
bloba544693c27e0670f53149720000e73f0b41f6c6c
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"
38 #include <string.h>
39 #include <expat.h>
40 #include <glib/gprintf.h>
42 #define LAUGH_NODE_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_NODE, LaughNodePrivate))
44 #define LAUGH_DOCUMENT_GET_PRIVATE(obj) \
45 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_DOCUMENT, LaughDocumentPrivate))
47 enum
49 INITIALIZED,
50 STARTED,
51 STOPPED,
53 LAST_SIGNAL
56 typedef struct _DocumentBuilder
58 LaughNode *top_node;
59 LaughNode *current;
60 } DocumentBuilder;
62 typedef struct _LaughIdMapping
64 GHashTable *string_id;
65 GHashTable *id_string;
66 long string_unknown;
67 } LaughIdMapping;
69 static guint laugh_node_signals[LAST_SIGNAL] = { 0 };
71 static LaughIdMapping _laugh_tag_map;
72 static LaughIdMapping _laugh_attr_map;
75 /*struct _LaughNodePrivate
77 };*/
79 static gpointer laugh_node_parent_class = ((void *)0);
81 static void laugh_node_finalize (GObject *object)
83 G_OBJECT_CLASS (laugh_node_parent_class)->finalize (object);
86 static void laugh_node_dispose (GObject *object)
88 LaughNode *node = LAUGH_NODE(object);
89 LaughNode *child;
91 if (node->attributes)
92 g_hash_table_destroy (node->attributes);
94 for (child = node->first_child; child; ) {
95 LaughNode *tmp = child->next_sibling;
96 g_object_unref (child);
97 child = tmp;
100 G_OBJECT_CLASS (laugh_node_parent_class)->dispose (object);
103 void laugh_node_base_set_attribute (LaughNode *node,
104 LaughNodeAttributeId attr, const gchar *val, gpointer *undo)
106 /*TODO: if val is NULL and undo is not NULL, free *undo and
107 * set *undo to old value*/
109 /* TODO: create a dynamic attribute list, create or handle undo */
111 /*This is ugly, during initialize g_hash_table_foreach call us */
112 if (node->state > LaughStateInit && node->attributes)
113 g_hash_table_insert (node->attributes,
114 (gpointer) (long) attr, (gpointer) val);
117 static
118 void _laugh_node_set_attribute (LaughNode *node,
119 LaughNodeAttributeId attr, const gchar *val, gpointer *undo)
121 laugh_node_base_set_attribute (node, attr, val, undo);
124 static const gchar *_laugh_node_get_attribute (LaughNode *node,
125 LaughNodeAttributeId att)
127 if (node->attributes)
128 return (gchar *) g_hash_table_lookup (
129 node->attributes, (gpointer) (long) att);
130 return NULL;
133 static void _laugh_node_init (LaughNode *node, LaughInitializer *initializer)
135 LaughNode *child;
137 if (node->attributes)
138 g_hash_table_foreach (node->attributes, laugh_attributes_set, node);
140 for (child = node->first_child; child; child = child->next_sibling)
141 laugh_node_init (child, initializer);
144 static void _laugh_add_mapping (LaughIdMapping *m, long id, const gchar *s)
146 g_hash_table_insert (m->string_id, (gpointer) s, (gpointer) id);
147 g_hash_table_insert (m->id_string, (gpointer) id, (gpointer) s);
150 static void laugh_node_class_init (LaughNodeClass *klass)
152 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
154 laugh_node_parent_class = g_type_class_peek_parent (klass);
156 gobject_class->finalize = laugh_node_finalize;
157 gobject_class->dispose = laugh_node_dispose;
158 klass->set_attribute = _laugh_node_set_attribute;
159 klass->get_attribute = _laugh_node_get_attribute;
160 klass->init = _laugh_node_init;
162 /*g_type_class_add_private (gobject_class, sizeof (LaughNodePrivate));*/
164 laugh_node_signals[INITIALIZED] =
165 g_signal_new ("initialized",
166 G_TYPE_FROM_CLASS (gobject_class),
167 G_SIGNAL_RUN_LAST,
168 G_STRUCT_OFFSET (LaughNodeClass, initialized),
169 NULL, NULL,
170 g_cclosure_marshal_VOID__VOID,
171 G_TYPE_NONE, 0);
173 laugh_node_signals[STARTED] =
174 g_signal_new ("started",
175 G_TYPE_FROM_CLASS (gobject_class),
176 G_SIGNAL_RUN_LAST,
177 G_STRUCT_OFFSET (LaughNodeClass, started),
178 NULL, NULL,
179 g_cclosure_marshal_VOID__VOID,
180 G_TYPE_NONE, 0);
182 laugh_node_signals[STOPPED] =
183 g_signal_new ("stopped",
184 G_TYPE_FROM_CLASS (gobject_class),
185 G_SIGNAL_RUN_LAST,
186 G_STRUCT_OFFSET (LaughNodeClass, stopped),
187 NULL, NULL,
188 g_cclosure_marshal_VOID__VOID,
189 G_TYPE_NONE, 0);
191 _laugh_tag_map.string_id = g_hash_table_new (g_str_hash, g_str_equal);
192 _laugh_tag_map.id_string = g_hash_table_new (NULL, NULL);
193 _laugh_tag_map.string_unknown = LaughTagIdLast;
195 _laugh_attr_map.string_id = g_hash_table_new (g_str_hash, g_str_equal);
196 _laugh_attr_map.id_string = g_hash_table_new (NULL, NULL);
197 _laugh_attr_map.string_unknown = LaughAttrIdLast;
199 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdDocument, "document");
200 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdSmil, "smil");
201 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdHead, "head");
202 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdLayout, "layout");
203 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdRootLayout, "root-layout");
204 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdRegion, "region");
205 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdBody, "body");
206 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdSeq, "seq");
207 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdPar, "par");
208 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdExcl, "excl");
209 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdSwitch, "switch");
210 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdAudio, "audio");
211 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdImg, "img");
212 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdRef, "ref");
213 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdText, "text");
214 _laugh_add_mapping (&_laugh_tag_map, LaughTagIdVideo, "video");
216 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdId, "id");
217 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdName, "name");
218 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdLeft, "left");
219 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTop, "top");
220 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdWidth, "width");
221 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdHeight, "height");
222 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdRight, "right");
223 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBottom, "bottom");
224 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBgColor, "background-color");
225 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBgColor1, "backgroundColor");
226 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBgImage, "backgroundImage");
227 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdShowBg, "showBackground");
228 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdZIndex, "z-index");
229 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBegin, "begin");
230 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdDur, "dur");
231 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdEnd, "end");
232 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdEndSync, "endSync");
233 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdRepeat, "repeat");
234 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdSrc, "src");
235 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdType, "type");
236 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdSubType, "subType");
237 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFill, "fil");
238 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFit, "fit");
239 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdPeers, "peers");
240 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdHigher, "higher");
241 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdLower, "lower");
242 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdPauseDisplay, "pauseDisplay");
243 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdHRef, "href");
244 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdCoord, "coord");
245 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTransIn, "trans-in");
246 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTransOut, "trans-out");
247 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdSystemBitrate, "system-bitrate");
248 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdSensitivity, "sensitivity");
249 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFontColor, "fontColor");
250 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFontSize, "fontSize");
251 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdHAlign, "hAlign");
252 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdVAlign, "vAlign");
253 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdCharset, "charset");
254 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdBgOpacity, "backgroundOpacity");
255 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTargetAttr, "targetAttribute");
256 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTarget, "target");
257 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdAttr, "attribute");
258 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdAttrName, "attributeName");
259 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdTo, "to");
260 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdChangeBy, "changed-by");
261 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdFrom, "from");
262 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdValues, "values");
263 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdCalcMode, "calcMode");
264 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdKeyTimes, "keyTimes");
265 _laugh_add_mapping (&_laugh_attr_map, LaughAttrIdKeySplines, "keySplines");
268 static void laugh_node_instance_init (GTypeInstance *instance, gpointer g_class)
270 LaughNode *self = (LaughNode *)instance;
272 /*self->priv = LAUGH_NODE_GET_PRIVATE (self);*/
274 self->state = LaughStateInit;
275 self->parent_node = NULL;
276 self->first_child = NULL;
277 self->previous_sibling = NULL;
278 self->next_sibling = NULL;
279 self->attributes = NULL;
282 GType laugh_node_get_type (void)
284 static GType type = 0;
285 if (type == 0) {
286 static const GTypeInfo info = {
287 sizeof (LaughNodeClass),
288 NULL, /* base_init */
289 NULL, /* base_finalize */
290 (GClassInitFunc) laugh_node_class_init, /* class_init */
291 NULL, /* class_finalize */
292 NULL, /* class_data */
293 sizeof (LaughNode),
294 0, /* n_preallocs */
295 laugh_node_instance_init /* instance_init */
297 type = g_type_register_static (G_TYPE_OBJECT,
298 "LaughNodeType",
299 &info, 0);
301 return type;
304 struct _LaughDocumentPrivate
306 gchar *uri;
307 LaughIO *io;
310 static gpointer laugh_document_parent_class = ((void *)0);
312 static void laugh_document_finalize (GObject *object)
314 G_OBJECT_CLASS (laugh_document_parent_class)->finalize (object);
317 static void laugh_document_dispose (GObject *object)
319 LaughDocument *document = LAUGH_DOCUMENT(object);
320 LaughTimingSegment *segment = document->timing;
322 g_free (document->priv->uri);
324 G_OBJECT_CLASS (laugh_document_parent_class)->dispose (object);
326 laugh_timing_segment_delete (segment);
329 static
330 void _lauch_document_mime_type (LaughIO *io, const gchar *mime, gpointer d)
332 g_printf ("lauch_document_mime_type: %s\n", mime);
333 if (strcmp (mime, "application/smil")) {
334 LaughNode *node = LAUGH_NODE (d);
336 g_printerr ("not a smil document\n");
337 laugh_io_cancel (io);
339 g_object_unref (G_OBJECT (io));
341 laugh_node_base_emit_initialized (node);
345 static void
346 _lauch_document_completed (LaughIO *io, gsize sz, gpointer data, gpointer d)
348 LaughInitializer *initializer = (LaughInitializer *)d;
349 LaughNode *node = initializer->node;
351 g_printf ("_lauch_document_completed: %u\n", sz);
352 if (data) {
353 laugh_node_set_inner_xml (node, (const gchar *) data);
355 g_free (data);
357 if (node->first_child)
358 laugh_node_init (node->first_child, initializer);
360 g_object_unref (G_OBJECT (io));
362 /*TODO wait for initialized signals*/
363 laugh_node_base_emit_initialized (node);
366 static void _laugh_document_init (LaughNode *doc, LaughInitializer *initializer)
368 LaughDocument *document = (LaughDocument *) doc;
369 LaughDocumentPrivate *priv = document->priv;
371 initializer->node = doc;
372 initializer->parent_segment = document->timing;
373 priv->io = laugh_io_new (priv->uri);
375 g_signal_connect (G_OBJECT (priv->io), "mime-type",
376 (GCallback) _lauch_document_mime_type, (gpointer) doc);
377 g_signal_connect (G_OBJECT (priv->io), "completed",
378 (GCallback) _lauch_document_completed, (gpointer) initializer);
380 laugh_io_open (priv->io);
383 static void laugh_document_class_init (LaughDocumentClass *klass)
385 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
386 LaughNodeClass *node_class = (LaughNodeClass *) klass;
388 laugh_document_parent_class = g_type_class_peek_parent (klass);
390 gobject_class->finalize = laugh_document_finalize;
391 gobject_class->dispose = laugh_document_dispose;
392 node_class->init = _laugh_document_init;
394 g_type_class_add_private (gobject_class, sizeof (LaughDocumentPrivate));
397 static
398 void laugh_document_instance_init (GTypeInstance *instance, gpointer g_class)
400 LaughDocument *self = (LaughDocument *)instance;
402 self->priv = LAUGH_DOCUMENT_GET_PRIVATE (self);
403 self->timing = g_new0 (LaughTimingSegment, 1);
404 self->timing->node = (LaughNode *) self;
407 GType laugh_document_get_type (void)
409 static GType type = 0;
410 if (type == 0) {
411 static const GTypeInfo info = {
412 sizeof (LaughDocumentClass),
413 NULL, /* base_init */
414 NULL, /* base_finalize */
415 (GClassInitFunc) laugh_document_class_init, /* class_init */
416 NULL, /* class_finalize */
417 NULL, /* class_data */
418 sizeof (LaughDocument),
419 0, /* n_preallocs */
420 laugh_document_instance_init /* instance_init */
422 type = g_type_register_static (LAUGH_TYPE_NODE,
423 "LaughDocumentType",
424 &info, 0);
426 return type;
429 static long _laugh_mapping_from_string (LaughIdMapping *map, const gchar *tag)
431 gpointer id = g_hash_table_lookup (map->string_id, (gpointer) tag);
432 if (!id) {
433 id = (gpointer) map->string_unknown++;
434 _laugh_add_mapping (map, (long) id, g_strdup (tag));
436 return (long) id;
439 LaughNodeTagId laugh_tag_from_string (const gchar *tag)
441 return (LaughNodeTagId) _laugh_mapping_from_string (&_laugh_tag_map, tag);
444 const gchar *laugh_tag_from_id (LaughNodeTagId id)
446 return (const gchar *) g_hash_table_lookup (_laugh_tag_map.id_string,
447 (gpointer) id);
450 LaughNodeAttributeId laugh_attribute_from_string (const gchar *attr)
452 return (LaughNodeAttributeId)_laugh_mapping_from_string (
453 &_laugh_attr_map, attr);
456 const gchar *laugh_attribute_from_id (LaughNodeAttributeId id)
458 return (const gchar *) g_hash_table_lookup (_laugh_attr_map.id_string,
459 (gpointer) id);
462 LaughInitializer *laugh_dom_initializer_new (ClutterContainer *parent,
463 guint width, guint height)
465 LaughInitializer *initializer = g_new0 (LaughInitializer, 1);
467 initializer->parent_region = parent;
468 initializer->parent_width = width;
469 initializer->parent_height = height;
471 return initializer;
474 LaughNode *laugh_node_new (LaughDocument *document, LaughNodeTagId id,
475 GHashTable *attrs)
477 LaughNode *node = LAUGH_NODE (g_object_new (LAUGH_TYPE_NODE, NULL));
479 laugh_node_base_construct (document, node, id, attrs);
481 return node;
484 LaughDocument *laugh_document_new (const gchar *uri)
486 LaughDocument *d = LAUGH_DOCUMENT(g_object_new (LAUGH_TYPE_DOCUMENT, NULL));
487 LaughNode *node = (LaughNode *)d;
489 laugh_node_base_construct (d, node, LaughTagIdDocument, NULL);
490 d->priv->uri = g_strdup (uri);
492 return d;
495 void laugh_node_base_construct (LaughDocument *document, LaughNode *node,
496 LaughNodeTagId id, GHashTable *attributes)
498 node->id = id;
499 node->document = document;
500 node->attributes = attributes;
503 void laugh_attributes_set (gpointer key, gpointer val, gpointer data)
505 laugh_node_set_attribute ((LaughNode *)data,
506 (LaughNodeAttributeId) (long) key, (const gchar *) val, NULL);
509 void laugh_node_base_emit_initialized (LaughNode *node)
511 g_signal_emit (node, laugh_node_signals[INITIALIZED], 0);
514 const gchar *laugh_node_get_attribute (LaughNode *node,
515 LaughNodeAttributeId attribute)
517 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
519 return klass->get_attribute (node, attribute);
522 void laugh_node_set_attribute (LaughNode *node,
523 LaughNodeAttributeId attr, const gchar *value, gpointer *undo)
525 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
527 klass->set_attribute (node, attr, value, undo);
530 static
531 void _laugh_xml_start_tag (void *data, const char *tag, const char **attr)
533 DocumentBuilder *builder = (DocumentBuilder *) data;
534 LaughNode *node;
535 GHashTable *attrs = NULL;
536 LaughNodeTagId id = laugh_tag_from_string (tag);
538 if (attr && attr[0]) {
539 int i;
540 attrs = g_hash_table_new_full (
541 NULL, NULL, NULL, (GDestroyNotify) g_free);
542 for (i = 0; attr[i]; i += 2)
543 g_hash_table_insert (attrs,
544 (gpointer) (long) laugh_attribute_from_string (attr[i]),
545 g_strdup (attr[i+1]));
547 switch (id) {
548 case LaughTagIdLayout:
549 case LaughTagIdRootLayout:
550 case LaughTagIdRegion:
551 node = laugh_layout_new (builder->top_node->document, id, attrs);
552 break;
553 case LaughTagIdBody:
554 case LaughTagIdSeq:
555 case LaughTagIdPar:
556 case LaughTagIdExcl:
557 node = laugh_timing_container_new (builder->top_node->document,
558 id, attrs);
559 break;
560 default:
561 node = laugh_node_new (builder->top_node->document, id, attrs);
563 laugh_node_append_child (builder->current, node);
565 builder->current = node;
568 static void _laugh_xml_end_tag (void *data, const char *tag)
570 DocumentBuilder *builder = (DocumentBuilder *) data;
572 if (laugh_tag_from_string (tag) != builder->current->id)
573 g_printerr ("unmatched tag %s, expects %s\n",
574 tag, laugh_tag_from_id (builder->current->id));
575 else if (builder->current != builder->top_node)
576 builder->current = builder->current->parent_node;
577 else
578 g_printerr ("spurious end tag %s", tag);
581 void laugh_node_set_inner_xml (LaughNode *node, const gchar *xml)
583 DocumentBuilder builder;
584 XML_Parser parser = XML_ParserCreate (0L);
586 /*TODO: clear all node's child nodes*/
587 builder.top_node = node;
588 builder.current = node;
590 XML_SetUserData (parser, &builder);
591 XML_SetElementHandler (parser, _laugh_xml_start_tag, _laugh_xml_end_tag);
593 if (XML_Parse (parser, xml, strlen (xml), TRUE) == XML_STATUS_ERROR) {
594 /*TODO: clear all*/
597 XML_ParserFree(parser);
600 static void _laugh_node_get_outer_xml (LaughNode *node, GString *buffer, int sp)
602 gchar *indent = "";
604 if (sp > 0) {
605 indent = (gchar *) g_malloc (sp + 1);
606 memset (indent, ' ', sp);
607 indent[sp] = 0;
608 g_string_append (buffer, indent);
611 g_string_append_c (buffer, '<');
612 g_string_append (buffer, laugh_tag_from_id (node->id));
613 if (node->attributes) {
614 GHashTableIter iter;
615 gpointer key, value;
616 g_hash_table_iter_init (&iter, node->attributes);
617 while (g_hash_table_iter_next (&iter, &key, &value)) {
618 g_string_append_c (buffer, ' ');
619 g_string_append (buffer, laugh_attribute_from_id ((long) key));
620 g_string_append (buffer, "=\"");
621 g_string_append (buffer, (const gchar *)value);/*TODO escape*/
622 g_string_append_c (buffer, '"');
625 if (node->first_child) {
626 LaughNode *child;
627 g_string_append (buffer, ">\n");
628 for (child = node->first_child; child; child = child->next_sibling)
629 _laugh_node_get_outer_xml (child, buffer, sp+2);
630 g_string_append (buffer, indent);
631 g_string_append (buffer, "</");
632 g_string_append (buffer, laugh_tag_from_id (node->id));
633 g_string_append (buffer, ">\n");
634 } else {
635 g_string_append (buffer, "/>\n");
638 if (*indent)
639 g_free (indent);
642 gchar *laugh_node_get_inner_xml (LaughNode *node)
644 GString *buffer = g_string_new ("");
645 LaughNode *child;
646 gchar *xml;
648 for (child = node->first_child; child; child = child->next_sibling)
649 _laugh_node_get_outer_xml (child, buffer, 0);
651 xml = g_string_free (buffer, FALSE);
653 return xml;
656 void laugh_node_append_child (LaughNode *node, LaughNode *child)
658 LaughNode *n = node->last_child;
660 if (n) {
661 n->next_sibling = child;
662 child->previous_sibling = n;
663 child->next_sibling = NULL;
664 } else {
665 node->first_child = child;
666 child->previous_sibling = NULL;
667 child->next_sibling = NULL;
669 node->last_child = child;
670 child->parent_node = node;
673 void laugh_node_init (LaughNode *node, LaughInitializer *initializer)
675 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(node);
677 klass->init (node, initializer);
680 #ifdef LAUGH_DOM_TEST
681 /* 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 */
682 #include <unistd.h>
683 #include <sys/types.h>
684 #include <sys/stat.h>
685 #include <fcntl.h>
687 static GMainLoop *mainloop;
689 void initialized_callback (LaughNode *node, gpointer d)
691 gchar *xml = laugh_node_get_inner_xml (node);
692 g_printf ("cb initialized\n%s\n", xml);
694 g_free (xml);
696 g_main_loop_quit (mainloop);
699 int main (int argc, char **argv)
701 LaughDocument *doc;
702 LaughInitializer *initializer;
704 if (argc < 2) {
705 g_printerr ("usage %s uri\n", argv[0]);
706 return 1;
709 g_thread_init (NULL);
710 g_type_init ();
712 doc = laugh_document_new (argv[1]);
713 initializer = laugh_dom_initializer_new (NULL, 640, 480);
715 g_signal_connect (G_OBJECT (doc), "initialized",
716 (GCallback) initialized_callback, (gpointer) 0);
718 laugh_node_init (LAUGH_NODE (doc), initializer);
720 mainloop = g_main_loop_new (NULL, TRUE);
721 g_main_loop_run (mainloop);
723 g_object_unref (G_OBJECT (doc));
724 g_free (initializer);
726 return 0;
729 #endif