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"
39 #include <glib/gprintf.h>
41 #define LAUGH_NODE_GET_PRIVATE(obj) \
42 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_NODE, LaughNodePrivate))
43 #define LAUGH_DOCUMENT_GET_PRIVATE(obj) \
44 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_DOCUMENT, LaughDocumentPrivate))
55 typedef struct _DocumentBuilder
61 static guint laugh_node_signals
[LAST_SIGNAL
] = { 0 };
63 static GHashTable
*_laugh_tag_id
;
64 static GHashTable
*_laugh_id_tag
;
65 static long _laugh_tag_unknown
= (long) LaughTagIdLast
;
67 struct _LaughNodePrivate
69 GHashTable
*attributes
;
72 static gpointer laugh_node_parent_class
= ((void *)0);
74 static void laugh_node_finalize (GObject
*object
)
76 G_OBJECT_CLASS (laugh_node_parent_class
)->finalize (object
);
79 static void laugh_node_dispose (GObject
*object
)
81 LaughNode
*node
= LAUGH_NODE(object
);
84 if (node
->priv
->attributes
)
85 g_hash_table_destroy (node
->priv
->attributes
);
87 for (child
= node
->first_child
; child
; ) {
88 LaughNode
*tmp
= child
->next_sibling
;
89 g_object_unref (child
);
93 G_OBJECT_CLASS (laugh_node_parent_class
)->dispose (object
);
97 void _laugh_node_set_attribute (LaughNode
*node
, GQuark att
, const gchar
*val
)
99 LaughNodePrivate
*priv
= node
->priv
;
101 /* TODO: create a dynamic attribute list */
102 if (priv
->attributes
)
103 g_hash_table_insert (priv
->attributes
,
104 (gpointer
) (long) att
, (gpointer
) val
);
107 static const gchar
*_laugh_node_get_attribute (LaughNode
*node
, GQuark att
)
109 LaughNodePrivate
*priv
= node
->priv
;
111 /* TODO: create a dynamic attribute list */
112 if (priv
->attributes
)
113 return (gchar
*) g_hash_table_lookup (
114 priv
->attributes
, (gpointer
) (long) att
);
118 static void _laugh_node_init (LaughNode
*node
, LaughInitializer
*initializer
)
122 for (child
= node
->first_child
; child
; child
= child
->next_sibling
)
123 laugh_node_init (child
, initializer
);
126 static void _laugh_register_tag (LaughNodeTagId id
, const gchar
*tag
)
128 g_hash_table_insert (_laugh_tag_id
, (gpointer
) tag
, (gpointer
) id
);
129 g_hash_table_insert (_laugh_id_tag
, (gpointer
) id
, (gpointer
) tag
);
132 static gboolean
_laugh_cmp_string (gconstpointer a
, gconstpointer b
)
134 return !strcmp ((const gchar
*) a
, (const gchar
*) b
);
137 static void laugh_node_class_init (LaughNodeClass
*klass
)
139 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
141 laugh_node_parent_class
= g_type_class_peek_parent (klass
);
143 gobject_class
->finalize
= laugh_node_finalize
;
144 gobject_class
->dispose
= laugh_node_dispose
;
145 klass
->set_attribute
= _laugh_node_set_attribute
;
146 klass
->get_attribute
= _laugh_node_get_attribute
;
147 klass
->init
= _laugh_node_init
;
149 g_type_class_add_private (gobject_class
, sizeof (LaughNodePrivate
));
151 laugh_node_signals
[INITIALIZED
] =
152 g_signal_new ("initialized",
153 G_TYPE_FROM_CLASS (gobject_class
),
155 G_STRUCT_OFFSET (LaughNodeClass
, initialized
),
157 g_cclosure_marshal_VOID__VOID
,
160 laugh_node_signals
[STARTED
] =
161 g_signal_new ("started",
162 G_TYPE_FROM_CLASS (gobject_class
),
164 G_STRUCT_OFFSET (LaughNodeClass
, started
),
166 g_cclosure_marshal_VOID__VOID
,
169 laugh_node_signals
[STOPPED
] =
170 g_signal_new ("stopped",
171 G_TYPE_FROM_CLASS (gobject_class
),
173 G_STRUCT_OFFSET (LaughNodeClass
, stopped
),
175 g_cclosure_marshal_VOID__VOID
,
177 _laugh_tag_id
= g_hash_table_new (g_str_hash
, _laugh_cmp_string
);
178 _laugh_id_tag
= g_hash_table_new (NULL
, NULL
);
179 _laugh_register_tag (LaughTagIdSmil
, "smil");
180 _laugh_register_tag (LaughTagIdHead
, "head");
181 _laugh_register_tag (LaughTagIdLayout
, "layout");
182 _laugh_register_tag (LaughTagIdRootLayout
, "root-layout");
183 _laugh_register_tag (LaughTagIdRegion
, "region");
184 _laugh_register_tag (LaughTagIdBody
, "body");
185 _laugh_register_tag (LaughTagIdSeq
, "seq");
186 _laugh_register_tag (LaughTagIdPar
, "par");
187 _laugh_register_tag (LaughTagIdExcl
, "excl");
188 _laugh_register_tag (LaughTagIdSwitch
, "switch");
189 _laugh_register_tag (LaughTagIdAudio
, "audio");
190 _laugh_register_tag (LaughTagIdImg
, "img");
191 _laugh_register_tag (LaughTagIdRef
, "ref");
192 _laugh_register_tag (LaughTagIdText
, "text");
193 _laugh_register_tag (LaughTagIdVideo
, "video");
196 static void laugh_node_instance_init (GTypeInstance
*instance
, gpointer g_class
)
198 LaughNode
*self
= (LaughNode
*)instance
;
200 self
->priv
= LAUGH_NODE_GET_PRIVATE (self
);
202 self
->parent_node
= NULL
;
203 self
->first_child
= NULL
;
204 self
->previous_sibling
= NULL
;
205 self
->next_sibling
= NULL
;
206 self
->priv
->attributes
= NULL
;
209 GType
laugh_node_get_type (void)
211 static GType type
= 0;
213 static const GTypeInfo info
= {
214 sizeof (LaughNodeClass
),
215 NULL
, /* base_init */
216 NULL
, /* base_finalize */
217 (GClassInitFunc
) laugh_node_class_init
, /* class_init */
218 NULL
, /* class_finalize */
219 NULL
, /* class_data */
222 laugh_node_instance_init
/* instance_init */
224 type
= g_type_register_static (G_TYPE_OBJECT
,
231 struct _LaughDocumentPrivate
237 static gpointer laugh_document_parent_class
= ((void *)0);
239 static void laugh_document_finalize (GObject
*object
)
241 G_OBJECT_CLASS (laugh_document_parent_class
)->finalize (object
);
244 static void laugh_document_dispose (GObject
*object
)
246 LaughDocument
*document
= LAUGH_DOCUMENT(object
);
248 g_free (document
->priv
->uri
);
250 G_OBJECT_CLASS (laugh_document_parent_class
)->dispose (object
);
254 void _lauch_document_mime_type (LaughIO
*io
, const gchar
*mime
, gpointer d
)
256 g_printf ("lauch_document_mime_type: %s\n", mime
);
257 if (strcmp (mime
, "application/smil")) {
258 LaughNode
*node
= LAUGH_NODE (d
);
260 g_printerr ("not a smil document\n");
261 laugh_io_cancel (io
);
263 g_object_unref (G_OBJECT (io
));
265 laugh_node_base_emit_initialized (node
);
270 _lauch_document_completed (LaughIO
*io
, gsize sz
, gpointer data
, gpointer d
)
272 LaughNode
*node
= LAUGH_NODE (d
);
274 g_printf ("_lauch_document_completed: %u\n", sz
);
276 laugh_node_set_inner_xml (node
, (const gchar
*) data
);
280 g_object_unref (G_OBJECT (io
));
282 /*TODO init children and wait for initialized signals*/
283 laugh_node_base_emit_initialized (node
);
286 static void _laugh_document_init (LaughNode
*doc
, LaughInitializer
*initializer
)
288 LaughDocumentPrivate
*priv
= LAUGH_DOCUMENT (doc
)->priv
;
290 priv
->io
= laugh_io_new (priv
->uri
);
292 g_signal_connect (G_OBJECT (priv
->io
), "mime-type",
293 (GCallback
) _lauch_document_mime_type
, (gpointer
) doc
);
294 g_signal_connect (G_OBJECT (priv
->io
), "completed",
295 (GCallback
) _lauch_document_completed
, (gpointer
) doc
);
297 laugh_io_open (priv
->io
);
300 static void laugh_document_class_init (LaughDocumentClass
*klass
)
302 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
303 LaughNodeClass
*node_class
= (LaughNodeClass
*) klass
;
305 laugh_document_parent_class
= g_type_class_peek_parent (klass
);
307 gobject_class
->finalize
= laugh_document_finalize
;
308 gobject_class
->dispose
= laugh_document_dispose
;
309 node_class
->init
= _laugh_document_init
;
311 g_type_class_add_private (gobject_class
, sizeof (LaughDocumentPrivate
));
315 void laugh_document_instance_init (GTypeInstance
*instance
, gpointer g_class
)
317 LaughDocument
*self
= (LaughDocument
*)instance
;
319 self
->priv
= LAUGH_DOCUMENT_GET_PRIVATE (self
);
322 GType
laugh_document_get_type (void)
324 static GType type
= 0;
326 static const GTypeInfo info
= {
327 sizeof (LaughDocumentClass
),
328 NULL
, /* base_init */
329 NULL
, /* base_finalize */
330 (GClassInitFunc
) laugh_document_class_init
, /* class_init */
331 NULL
, /* class_finalize */
332 NULL
, /* class_data */
333 sizeof (LaughDocument
),
335 laugh_document_instance_init
/* instance_init */
337 type
= g_type_register_static (LAUGH_TYPE_NODE
,
344 LaughNodeTagId
laugh_tag_from_string (const gchar
*tag
)
346 gpointer id
= g_hash_table_lookup (_laugh_tag_id
, (gpointer
) tag
);
348 id
= (gpointer
) _laugh_tag_unknown
++;
349 _laugh_register_tag ((LaughNodeTagId
) id
, g_strdup (tag
));
351 return (LaughNodeTagId
) (long) id
;
354 const gchar
*laugh_tag_from_id (LaughNodeTagId id
)
356 return (const gchar
*) g_hash_table_lookup (_laugh_id_tag
, (gpointer
) id
);
360 LaughNode
*laugh_node_new (LaughDocument
*document
, LaughNodeTagId id
,
363 LaughNode
*node
= LAUGH_NODE (g_object_new (LAUGH_TYPE_NODE
, NULL
));
365 laugh_node_base_init (document
, node
, id
, attrs
);
370 LaughDocument
*laugh_document_new (const gchar
*uri
)
372 LaughDocument
*d
= LAUGH_DOCUMENT(g_object_new (LAUGH_TYPE_DOCUMENT
, NULL
));
373 LaughNode
*node
= (LaughNode
*)d
;
375 laugh_node_base_init (d
, node
, LaughTagIdDocument
, NULL
);
376 d
->priv
->uri
= g_strdup (uri
);
381 void laugh_node_base_init (LaughDocument
*document
, LaughNode
*node
,
382 LaughNodeTagId id
, GHashTable
*attributes
)
385 node
->document
= document
;
386 node
->priv
->attributes
= attributes
;
389 void laugh_node_base_emit_initialized (LaughNode
*node
)
391 g_signal_emit (node
, laugh_node_signals
[INITIALIZED
], 0);
394 const gchar
*laugh_node_get_attribute (LaughNode
*node
, GQuark attribute
)
396 LaughNodeClass
*klass
= LAUGH_NODE_GET_CLASS(node
);
398 return klass
->get_attribute (node
, attribute
);
401 void laugh_node_set_attribute (LaughNode
*node
, GQuark attr
, const gchar
*value
)
403 LaughNodeClass
*klass
= LAUGH_NODE_GET_CLASS(node
);
405 klass
->set_attribute (node
, attr
, value
);
409 void _laugh_xml_start_tag (void *data
, const char *tag
, const char **attr
)
411 DocumentBuilder
*builder
= (DocumentBuilder
*) data
;
413 GHashTable
*attrs
= NULL
;
414 LaughNodeTagId id
= laugh_tag_from_string (tag
);
416 if (attr
&& attr
[0]) {
418 attrs
= g_hash_table_new_full (
419 NULL
, NULL
, NULL
, (GDestroyNotify
) g_free
);
420 for (i
= 0; attr
[i
]; i
+= 2)
421 g_hash_table_insert (attrs
,
422 (gpointer
) (long) g_quark_from_string (attr
[i
]),
423 g_strdup (attr
[i
+1]));
426 case LaughTagIdLayout
:
427 case LaughTagIdRootLayout
:
428 case LaughTagIdRegion
:
429 node
= laugh_layout_new (builder
->top_node
->document
, id
, attrs
);
432 node
= laugh_node_new (builder
->top_node
->document
, id
, attrs
);
434 laugh_node_append_child (builder
->current
, node
);
436 builder
->current
= node
;
439 static void _laugh_xml_end_tag (void *data
, const char *tag
)
441 DocumentBuilder
*builder
= (DocumentBuilder
*) data
;
443 if (laugh_tag_from_string (tag
) != builder
->current
->id
)
444 g_printerr ("unmatched tag %s, expects %s\n",
445 tag
, laugh_tag_from_id (builder
->current
->id
));
446 else if (builder
->current
!= builder
->top_node
)
447 builder
->current
= builder
->current
->parent_node
;
449 g_printerr ("spurious end tag %s", tag
);
452 void laugh_node_set_inner_xml (LaughNode
*node
, const gchar
*xml
)
454 DocumentBuilder builder
;
455 XML_Parser parser
= XML_ParserCreate (0L);
457 /*TODO: clear all node's child nodes*/
458 builder
.top_node
= node
;
459 builder
.current
= node
;
461 XML_SetUserData (parser
, &builder
);
462 XML_SetElementHandler (parser
, _laugh_xml_start_tag
, _laugh_xml_end_tag
);
464 if (XML_Parse (parser
, xml
, strlen (xml
), TRUE
) == XML_STATUS_ERROR
) {
468 XML_ParserFree(parser
);
471 static void _laugh_node_get_outer_xml (LaughNode
*node
, GString
*buffer
, int sp
)
473 LaughNodePrivate
*priv
= node
->priv
;
477 indent
= (gchar
*) g_malloc (sp
+ 1);
478 memset (indent
, ' ', sp
);
480 g_string_append (buffer
, indent
);
483 g_string_append_c (buffer
, '<');
484 g_string_append (buffer
, laugh_tag_from_id (node
->id
));
485 if (priv
->attributes
) {
488 g_hash_table_iter_init (&iter
, priv
->attributes
);
489 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
490 g_string_append_c (buffer
, ' ');
491 g_string_append (buffer
, g_quark_to_string ((GQuark
) (long) key
));
492 g_string_append (buffer
, "=\"");
493 g_string_append (buffer
, (const gchar
*)value
);/*TODO escape*/
494 g_string_append_c (buffer
, '"');
497 if (node
->first_child
) {
499 g_string_append (buffer
, ">\n");
500 for (child
= node
->first_child
; child
; child
= child
->next_sibling
)
501 _laugh_node_get_outer_xml (child
, buffer
, sp
+2);
502 g_string_append (buffer
, indent
);
503 g_string_append (buffer
, "</");
504 g_string_append (buffer
, laugh_tag_from_id (node
->id
));
505 g_string_append (buffer
, ">\n");
507 g_string_append (buffer
, "/>\n");
514 gchar
*laugh_node_get_inner_xml (LaughNode
*node
)
516 GString
*buffer
= g_string_new ("");
520 for (child
= node
->first_child
; child
; child
= child
->next_sibling
)
521 _laugh_node_get_outer_xml (child
, buffer
, 0);
523 xml
= g_string_free (buffer
, FALSE
);
528 void laugh_node_append_child (LaughNode
*node
, LaughNode
*child
)
530 LaughNode
*n
= node
->last_child
;
533 n
->next_sibling
= child
;
534 child
->previous_sibling
= n
;
535 child
->next_sibling
= NULL
;
537 node
->first_child
= child
;
538 child
->previous_sibling
= NULL
;
539 child
->next_sibling
= NULL
;
541 node
->last_child
= child
;
542 child
->parent_node
= node
;
545 void laugh_node_init (LaughNode
*node
, LaughInitializer
*initializer
)
547 LaughNodeClass
*klass
= LAUGH_NODE_GET_CLASS(node
);
549 klass
->init (node
, initializer
);
552 #ifdef LAUGH_DOM_TEST
553 /* gcc laugh-dom.c laugh-io.c -o domtest `pkg-config --libs --cflags gio-2.0 glib-2.0 gthread-2.0` -lexpat */
555 #include <sys/types.h>
556 #include <sys/stat.h>
559 static GMainLoop
*mainloop
;
561 void initialized_callback (LaughNode
*node
, gpointer d
)
563 gchar
*xml
= laugh_node_get_inner_xml (node
);
564 g_printf ("cb initialized\n%s\n", xml
);
568 g_main_loop_quit (mainloop
);
571 int main (int argc
, char **argv
)
574 LaughInitializer initializer
;
577 g_printerr ("usage %s uri\n", argv
[0]);
581 g_thread_init (NULL
);
584 doc
= laugh_document_new (argv
[1]);
586 g_signal_connect (G_OBJECT (doc
), "initialized",
587 (GCallback
) initialized_callback
, (gpointer
) 0);
589 laugh_node_init (LAUGH_NODE (doc
), &initializer
);
591 mainloop
= g_main_loop_new (NULL
, TRUE
);
592 g_main_loop_run (mainloop
);
594 g_object_unref (G_OBJECT (doc
));