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.
26 * @short_description: DOM bases classes.
33 #include "laugh-dom.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"
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))
59 typedef struct _DocumentBuilder
65 typedef struct _LaughIdMapping
67 GHashTable
*string_id
;
68 GHashTable
*id_string
;
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
;
97 static void _laugh_node_undo_free (gpointer key
, gpointer value
, gpointer data
)
100 (void) key
; (void) data
;
102 for (list
= (GSList
*) value
; list
; list
= list
->next
)
107 static void _laugh_node_children_clear (LaughNode
*node
)
111 for (child
= node
->first_child
; child
; ) {
112 LaughNode
*tmp
= child
->next_sibling
;
113 g_object_unref (child
);
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
;
149 gchar
*new_cur
= NULL
;
151 if (node
->attributes
)
152 cur
= g_hash_table_lookup (node
->attributes
, (gpointer
)(long) attr
);
154 node
->attributes
= g_hash_table_new (NULL
, NULL
);
160 new_cur
= g_strdup (val
);
161 g_hash_table_replace (node
->attributes
,
162 (gpointer
) (long) attr
, new_cur
);
164 g_hash_table_remove (node
->attributes
, (gpointer
) (long) attr
);
170 *(const gchar
**)undo
= new_cur
;
171 g_printf ("set undo to %s\n", new_cur
);
176 if (priv
->attribute_undo
)
177 ol
= (GSList
*) g_hash_table_lookup (
178 priv
->attribute_undo
, (gpointer
) (long) attr
);
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
);
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
);
194 if (undo_val
== cur
) {
196 g_hash_table_replace (node
->attributes
,
197 (gpointer
) (long) attr
, new_cur
);
200 ol
= g_slist_remove (ol
, undo_val
);
205 g_hash_table_remove (priv
->attribute_undo
,
206 (gpointer
) (long) attr
);
208 g_hash_table_replace (priv
->attribute_undo
,
209 (gpointer
) (long) attr
, ol
);
216 g_printf ("set undo to %s\n", new_cur
? new_cur
: "-");
217 *(gchar
**)undo
= new_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
);
234 static void _laugh_node_init (LaughNode
*node
, LaughInitializer
*initializer
)
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
),
287 G_STRUCT_OFFSET (LaughNodeClass
, initialized
),
289 g_cclosure_marshal_VOID__VOID
,
292 laugh_node_signals
[STARTED
] =
293 g_signal_new ("started",
294 G_TYPE_FROM_CLASS (gobject_class
),
296 G_STRUCT_OFFSET (LaughNodeClass
, started
),
298 g_cclosure_marshal_VOID__VOID
,
301 laugh_node_signals
[STOPPED
] =
302 g_signal_new ("stopped",
303 G_TYPE_FROM_CLASS (gobject_class
),
305 G_STRUCT_OFFSET (LaughNodeClass
, stopped
),
307 g_cclosure_marshal_VOID__VOID
,
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;
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 */
422 laugh_node_instance_init
/* instance_init */
424 type
= g_type_register_static (G_TYPE_OBJECT
,
431 struct _LaughDocumentPrivate
435 GHashTable
*element_id_table
;
436 guint element_id_last
;
437 ClutterContainer
*parent_actor
;
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
);
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
);
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
);
487 laugh_node_set_inner_xml (node
, (const gchar
*) 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
);
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
));
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;
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
),
581 laugh_document_instance_init
/* instance_init */
583 type
= g_type_register_static (LAUGH_TYPE_NODE
,
590 static long _laugh_mapping_from_string (LaughIdMapping
*map
, const gchar
*tag
)
592 gpointer id
= g_hash_table_lookup (map
->string_id
, (gpointer
) tag
);
594 id
= (gpointer
) map
->string_unknown
++;
595 _laugh_add_mapping (map
, (long) id
, g_strdup (tag
));
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
,
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
,
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
;
644 LaughNode
*laugh_node_new (LaughDocument
*document
, LaughNodeTagId id
,
647 LaughNode
*node
= LAUGH_NODE (g_object_new (LAUGH_TYPE_NODE
, NULL
));
649 laugh_node_base_construct (document
, node
, id
, attrs
);
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
);
665 void laugh_node_base_construct (LaughDocument
*document
, LaughNode
*node
,
666 LaughNodeTagId id
, GHashTable
*attributes
)
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
);
701 void _laugh_xml_start_tag (void *data
, const char *tag
, const char **attr
)
703 DocumentBuilder
*builder
= (DocumentBuilder
*) data
;
705 GHashTable
*attrs
= NULL
;
706 LaughNodeTagId id
= laugh_tag_from_string (tag
);
708 if (attr
&& attr
[0]) {
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]));
717 case LaughTagIdLayout
:
718 case LaughTagIdRootLayout
:
719 case LaughTagIdRegion
:
720 node
= laugh_layout_new (builder
->top_node
->document
, id
, attrs
);
726 node
= laugh_timing_container_new (builder
->top_node
->document
,
730 case LaughTagIdAnimate
:
731 case LaughTagIdAnimateMotion
:
732 node
= laugh_animate_new (builder
->top_node
->document
, id
, attrs
);
734 case LaughTagIdAudio
:
735 case LaughTagIdBrush
:
739 case LaughTagIdVideo
:
740 node
= laugh_media_new (builder
->top_node
->document
, id
, attrs
);
743 case LaughTagIdAnchor
:
745 node
= laugh_linking_new (builder
->top_node
->document
, id
, attrs
);
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
;
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
) {
785 XML_ParserFree(parser
);
788 static void _laugh_node_get_outer_xml (LaughNode
*node
, GString
*buffer
, int sp
)
793 indent
= (gchar
*) g_malloc (sp
+ 1);
794 memset (indent
, ' ', sp
);
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
) {
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
) {
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");
823 g_string_append (buffer
, "/>\n");
830 gchar
*laugh_node_get_inner_xml (LaughNode
*node
)
832 GString
*buffer
= g_string_new ("");
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
);
844 void laugh_node_append_child (LaughNode
*node
, LaughNode
*child
)
846 LaughNode
*n
= node
->last_child
;
849 n
->next_sibling
= child
;
850 child
->previous_sibling
= n
;
851 child
->next_sibling
= NULL
;
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
);
874 _laugh_node_start (node
);
877 void laugh_node_stop (LaughNode
*node
)
879 LaughNodeClass
*klass
= LAUGH_NODE_GET_CLASS(node
);
883 _laugh_node_stop (node
);
886 void laugh_node_freeze (LaughNode
*node
)
888 LaughNodeClass
*klass
= LAUGH_NODE_GET_CLASS(node
);
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
);
900 id
= ++priv
->element_id_last
;
901 g_hash_table_insert (priv
->element_id_table
,
902 g_strdup (str
), (gpointer
) (long) id
);
908 LaughNode
*_laugh_node_get_element_by_id (LaughNode
*node
, const guint id
)
912 if (id
== node
->element_id
)
915 for (child
= node
->first_child
; child
; child
= child
->next_sibling
) {
916 LaughNode
*n
= _laugh_node_get_element_by_id (child
, id
);
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
);
939 return klass
->role (node
, type
);
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
);
952 gchar
*new_uri
= laugh_io_uri_resolve (uri
, priv
->uri
);
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
);
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
;
980 g_printerr ("laugh_document_open: no uri set\n");
985 parent
= priv
->parent_actor
;
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
));
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 */
1006 #include <sys/types.h>
1007 #include <sys/stat.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
);
1019 g_main_loop_quit (mainloop
);
1022 int main (int argc
, char **argv
)
1025 LaughInitializer
*initializer
;
1028 g_printerr ("usage %s uri\n", argv
[0]);
1032 g_thread_init (NULL
);
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
);