Make sure to get the timing tree in sane state after an internal jump
[laugh.git] / src / laugh-timing.c
blob077e810a72406e78cf5360d1801bfe15bd41e7fc
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 #include <string.h>
25 #include <glib/gmessages.h>
26 #include <glib/gprintf.h>
27 #include <clutter/clutter-main.h>
29 #include "laugh-timing.h"
31 #define LAUGH_TIMING_CONTAINER_GET_PRIVATE(obj) \
32 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_TIMING_CONTAINER, LaughTimingContainerPrivate))
35 static gpointer laugh_timing_container_parent_class = ((void *)0);
37 static void laugh_timing_container_finalize (GObject *object)
39 G_OBJECT_CLASS (laugh_timing_container_parent_class)->finalize (object);
42 static void laugh_timing_container_dispose (GObject *object)
44 LaughTimingContainer *self = LAUGH_TIMING_CONTAINER(object);
45 LaughRoleTiming *segment = self->timing_role;
47 G_OBJECT_CLASS (laugh_timing_container_parent_class)->dispose (object);
49 laugh_timing_role_delete (segment);
52 static void
53 _laugh_timing_container_init (LaughNode *node, LaughInitializer *initializer)
55 LaughTimingContainer *self = (LaughTimingContainer *) node;
56 LaughNode *child;
57 LaughRoleTiming *parent_segment = initializer->parent_segment;
59 if (node->attributes)
60 g_hash_table_foreach (node->attributes, laugh_attributes_set, node);
62 laugh_timing_role_child_add (initializer->parent_segment, self->timing_role);
64 initializer->parent_segment = self->timing_role;
66 for (child = node->first_child; child; child = child->next_sibling)
67 laugh_node_init (child, initializer);
69 initializer->parent_segment = parent_segment;
72 static void _laugh_timing_container_set_attribute (LaughNode *node,
73 LaughNodeAttributeId att, const gchar *value, gpointer *undo)
75 const gchar *val = value;
76 LaughTimingContainer *self = (LaughTimingContainer *)node;
78 laugh_node_base_set_attribute (node, att, val, undo);
79 /*g_printf ("_laugh_timing_container_set_attribute %s=%s\n", laugh_attribute_from_id (att), val);*/
81 if (!value && undo)
82 val = *(const gchar **)undo;
84 laugh_timing_setting_set_attribute (self->timing_role, att, val);
87 static LaughRole *_laugh_timing_container_role (LaughNode *node, LaughRoleType type)
89 LaughTimingContainer *self = (LaughTimingContainer *) node;
91 switch (type) {
92 case LaughRoleTypeTiming:
93 return (LaughRole *) self->timing_role;
94 default:
95 return NULL;
99 static void laugh_timing_container_class_init (LaughTimingContainerClass *klass)
101 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
102 LaughNodeClass *node_class = (LaughNodeClass *) klass;
104 laugh_timing_container_parent_class = g_type_class_peek_parent (klass);
106 gobject_class->finalize = laugh_timing_container_finalize;
107 gobject_class->dispose = laugh_timing_container_dispose;
108 node_class->init = _laugh_timing_container_init;
109 node_class->set_attribute = _laugh_timing_container_set_attribute;
110 node_class->role = _laugh_timing_container_role;
112 /*g_type_class_add_private (gobject_class, sizeof (LaughTimingContainerPrivate));*/
115 static
116 void laugh_timing_container_instance_init (GTypeInstance *instance, gpointer g_class)
118 LaughTimingContainer *self = (LaughTimingContainer *)instance;
120 /*self->priv = LAUGH_TIMING_CONTAINER_GET_PRIVATE (self);*/
122 self->timing_role = laugh_timing_role_new ((LaughNode *) self);
125 GType laugh_timing_container_get_type (void)
127 static GType type = 0;
128 if (type == 0) {
129 static const GTypeInfo info = {
130 sizeof (LaughTimingContainerClass),
131 NULL, /* base_init */
132 NULL, /* base_finalize */
133 (GClassInitFunc) laugh_timing_container_class_init, /* class_init */
134 NULL, /* class_finalize */
135 NULL, /* class_data */
136 sizeof (LaughTimingContainer),
137 0, /* n_preallocs */
138 laugh_timing_container_instance_init /* instance_init */
140 type = g_type_register_static (LAUGH_TYPE_NODE,
141 "LaughTimingContainerType",
142 &info, 0);
144 return type;
147 static int _timing_parse_time (const gchar *tstr, int *value) {
148 int sign = 1;
149 int fp_seen = 0;
150 const gchar *nstart = NULL;
151 const gchar *p = tstr;
152 gchar *num;
154 if (!tstr)
155 return 0;
157 for ( ; *p; p++) {
158 if (*p == '+') {
159 if (nstart)
160 break;
161 else
162 sign = 1;
163 } else if (*p == '-') {
164 if (nstart)
165 break;
166 else
167 sign = -1;
168 } else if (*p >= '0' && *p <= '9') {
169 if (!nstart)
170 nstart = p;
171 } else if (*p == '.') {
172 if (fp_seen)
173 break;
174 fp_seen = 1;
175 if (!nstart)
176 nstart = p;
177 } else if (*p == ' ') {
178 if (nstart)
179 break;
180 } else
181 break;
183 if (!nstart)
184 return 0;
186 num = g_strndup (nstart, p - tstr);
187 *value = sign * (int) (1000 * g_strtod (nstart, NULL));
189 for ( ; *p; p++ ) {
190 if (*p == 'm') {
191 *value *= 60;
192 break;
193 } else if (*p == 'h') {
194 *value *= 3600;
195 break;
196 } else if (*p != ' ')
197 break;
200 g_free (num);
202 return 1;
205 static int _get_timing (const gchar *tstr, LaughTiming *timing) {
206 const gchar *orig = tstr;
207 gchar *lower;
208 const gchar *p = tstr;
209 gchar *idref = NULL;
211 for ( ; *p; ++p, ++orig) {
212 if (*p != ' ')
213 break;
216 timing->type = LaughTimingUnknown;
217 if (!p || !*p)
218 return 0;
220 lower = g_ascii_strdown (p, -1);
221 p = lower;
223 if (_timing_parse_time (lower, &timing->offset)) {
224 timing->type = LaughTimingTime;
225 } else if (!strncmp (lower, "id(", 3)) {
226 p = (const gchar *) strchr (lower + 3, ')');
227 if (p) {
228 idref = g_strndup (orig + 3, p - lower - 3);
229 p++;
231 if (*p) {
232 const char *q = strchr (p, '(');
233 if (q)
234 p = q;
236 } else if (!strncmp (lower, "indefinite", 10)) {
237 timing->type = LaughTimingIndefinite;
238 } else if (!strncmp (lower, "media", 5)) {
239 timing->type = LaughTimingMedia;
241 if (LaughTimingUnknown == timing->type) {
242 const gchar *idref_start = p;
243 if (!idref) {
244 int last_esc = 0;
245 for ( ; *p; p++) {
246 if (*p == '\\') {
247 last_esc = last_esc ? 0 : 1;
248 } else if (*p == '.' && !last_esc) {
249 break;
252 if (!*p)
253 idref = g_strdup (orig + (idref_start - lower));
254 else
255 idref = g_strndup (orig + (idref_start - lower), p - idref_start);
257 ++p;
258 if (idref) {
259 timing->element_id = idref;
260 if (_timing_parse_time (p, &timing->offset)) {
261 timing->type = LaughTimingStartSync;
262 } else if (*p && !strncmp (p, "end", 3)) {
263 timing->type = LaughTimingEndSync;
264 _timing_parse_time (p+3, &timing->offset);
265 } else if (*p && !strncmp (p, "begin", 5)) {
266 timing->type = LaughTimingStartSync;
267 _timing_parse_time (p+5, &timing->offset);
268 } else if (*p && !strncmp (p, "activateevent", 13)) {
269 timing->type = LaughTimingActivated;
270 _timing_parse_time (p+13, &timing->offset);
271 } else if (*p && !strncmp (p, "inboundsevent", 13)) {
272 timing->type = LaughTimingInbounds;
273 _timing_parse_time (p+13, &timing->offset);
274 } else if (*p && !strncmp (p, "outofboundsevent", 16)) {
275 timing->type = LaughTimingOutbounds;
276 _timing_parse_time (p+16, &timing->offset);
277 } else {
278 g_printerr ("get_timings no match %s", lower);
282 g_free (lower);
283 return 1;
286 LaughTiming *laugh_timing_new ()
288 return g_new0 (LaughTiming, 1);
291 static void _laugh_timing_element_destroyed (LaughTiming *timing, GObject *obj)
293 if (timing->handler_id) {
294 timing->handler_id = 0;
295 timing->element = 0;
296 } else {
297 g_printerr ("laugh_timing spurious destroy signal\n");
301 static gboolean _laugh_timing_timeout (gpointer data);
302 static void _laugh_timing_setting_started (LaughRoleTiming *segment);
303 static void _laugh_timing_setting_stopped (LaughRoleTiming *segment);
305 static void _laugh_timing_time_connect (LaughTiming *timing, int t, gpointer data)
307 timing->handler_id = g_timeout_add (t > 0 ? t : 0, _laugh_timing_timeout, data);
310 static void _laugh_timing_source_connect (LaughTiming *timing, LaughNode *source,
311 const gchar *signal, GCallback func, gpointer data)
313 timing->element = source;
314 g_object_weak_ref ((GObject *) source, _laugh_timing_element_destroyed, timing);
315 timing->handler_id = g_signal_connect (G_OBJECT (source), signal, func, data);
318 static void _laugh_timing_source_disconnect (LaughTiming *timing)
320 if (timing->handler_id) {
321 switch (timing->type) {
322 case LaughTimingTime:
323 g_source_remove (timing->handler_id);
324 break;
325 default:
326 if (timing->element) {
327 g_signal_handler_disconnect (timing->element, timing->handler_id);
328 g_object_weak_unref ((GObject *) timing->element,
329 _laugh_timing_element_destroyed, timing);
331 break;
333 timing->element = NULL;
334 timing->handler_id = 0;
338 void laugh_timing_delete (LaughTiming *timing)
340 _laugh_timing_source_disconnect (timing);
342 if (timing->element_id)
343 g_free (timing->element_id);
344 g_free (timing);
347 LaughRoleTiming *laugh_timing_role_new (LaughNode *node)
349 LaughRoleTiming *segment = g_new0 (LaughRoleTiming, 1);
351 segment->role.type = LaughRoleTypeTiming;
352 segment->node = node;
354 return segment;
357 void laugh_timing_role_source_disconnect (LaughRoleTiming *role)
359 if (role->begin)
360 _laugh_timing_source_disconnect (role->begin);
361 if (role->dur)
362 _laugh_timing_source_disconnect (role->dur);
363 if (role->end)
364 _laugh_timing_source_disconnect (role->end);
367 void laugh_timing_role_delete (LaughRoleTiming *segment)
369 LaughRoleTiming *parent = segment->parent;
371 if (segment->begin)
372 laugh_timing_delete (segment->begin);
373 if (segment->dur)
374 laugh_timing_delete (segment->dur);
375 if (segment->end)
376 laugh_timing_delete (segment->end);
378 if (segment->sub_segments)
379 g_printerr ("TimingSegment %s leaking sub segments\n",
380 laugh_tag_from_id (segment->node->id));
382 if (parent) {
383 GSList *s = parent->sub_segments;
384 for ( ;s; s = s->next) {
385 GSList *thread = (GSList *) s->data;
386 if (g_slist_find (thread, segment)) {
387 thread = g_slist_remove (thread, segment);
388 if (!thread)
389 parent->sub_segments = g_slist_remove (parent->sub_segments,
390 s->data);
391 else
392 s->data = thread;
393 break;
397 g_free (segment);
400 LaughTiming *laugh_timing_new_from_string (const gchar *value)
402 LaughTiming *t = laugh_timing_new ();
403 if (laugh_timing_set_value (t, value))
404 return t;
405 laugh_timing_delete (t);
406 return NULL;
409 void laugh_timing_role_child_add (LaughRoleTiming *s, LaughRoleTiming *child)
411 if (!s->sub_segments || LaughTagIdPar == s->node->id) {
412 s->sub_segments = g_slist_append (s->sub_segments,
413 g_slist_append (NULL, child));
414 } else {
415 GSList *last = g_slist_last (s->sub_segments);
416 last->data = g_slist_append ((GSList *) last->data, child);
419 child->parent = s;
422 gboolean laugh_timing_setting_set_attribute (LaughRoleTiming *segment,
423 LaughNodeAttributeId att, const gchar *val)
425 LaughTiming **timing;
427 switch (att) {
428 case LaughAttrIdBegin:
429 timing = &segment->begin;
430 break;
431 case LaughAttrIdDur:
432 timing = &segment->dur;
433 break;
434 case LaughAttrIdEnd:
435 timing = &segment->end;
436 break;
437 default:
438 return FALSE;
440 if (*timing) {
441 if (val) {
442 laugh_timing_set_value (*timing, val);
443 } else {
444 laugh_timing_delete (*timing);
445 *timing = NULL;
447 } else if (val) {
448 *timing = laugh_timing_new_from_string (val);
451 return TRUE;
454 void _laugh_timing_stop (LaughTiming *timing)
456 if (timing)
457 _laugh_timing_source_disconnect (timing);
460 static void _laugh_timing_role_dump (LaughRoleTiming *segment)
462 GSList *s = segment->sub_segments;
463 gboolean first = TRUE;
465 g_printf ("%s %d", laugh_tag_from_id (segment->node->id), segment->node->state);
467 if (s) {
468 g_printf ("[");
469 for ( ; s; s = s->next) {
470 GSList *s1 = (GSList *) s->data;
471 gboolean sub_first = TRUE;
472 if (!first)
473 g_printf (", ");
474 else
475 first = FALSE;
476 for ( ; s1; s1 = s1->next) {
477 if (!sub_first)
478 g_printf ("->");
479 else
480 sub_first = FALSE;
481 _laugh_timing_role_dump ((LaughRoleTiming *) s1->data);
484 g_printf ("]");
488 static
489 void _laugh_timing_setting_stop_freeze (LaughRoleTiming *role, LaughNodeState to_state)
491 GSList *s;
493 _laugh_timing_stop (role->begin);
494 _laugh_timing_stop (role->dur);
495 _laugh_timing_stop (role->end);
497 laugh_timing_role_source_disconnect (role);
499 /*TODO determine freeze */
500 for (s = role->sub_segments; s; s = s->next) {
501 GSList *sub;
502 for (sub = (GSList *) s->data; sub; sub = sub->next) {
503 LaughRoleTiming *seg = (LaughRoleTiming *) sub->data;
505 laugh_timing_role_source_disconnect (seg);
507 switch (to_state) {
508 case LaughStateStopped:
509 if (seg->active || LaughStateFreezed == seg->node->state)
510 laugh_timing_setting_stop (seg);
511 break;
512 case LaughStateFreezed:
513 if (seg->active)
514 /*FIXME: check fill*/
515 laugh_timing_setting_stop (seg);
516 break;
517 case LaughStateInitialized:
518 if (seg->node->state > LaughStateInitialized &&
519 seg->node->state != LaughStateStopped) {
520 _laugh_timing_setting_stop_freeze (seg, to_state);
521 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(seg->node);
522 if (klass->stop)
523 klass->stop (seg->node);
525 seg->node->state = LaughStateInitialized;
526 seg->active = FALSE;
527 break;
528 default:
529 break;
534 role->stop_time_stamp = clutter_get_timestamp ();
536 role->active = FALSE;
539 static void _laugh_timing_setting_stopped (LaughRoleTiming *segment)
541 GSList *s;
542 LaughRoleTiming *parent = segment->parent;
543 gboolean last_one = TRUE;
544 LaughRoleTiming *start_next = NULL;
545 gboolean freeze = FALSE;
546 gboolean found = FALSE;
548 g_printf ("laugh_timing_setting_stopped %s\n",
549 laugh_tag_from_id (segment->node->id));
551 if (parent) {
552 for (s = parent->sub_segments; s; s = s->next) {
553 GSList *sub = (GSList *) s->data;
554 GSList *l = found ? NULL : g_slist_find (sub, segment);
555 if (l) {
556 found = TRUE;
557 if (l->next) {
558 start_next = (LaughRoleTiming*) l->next->data;
559 last_one = FALSE;
560 } else {
561 const gchar *fill = laugh_node_get_attribute (
562 segment->node, LaughAttrIdFill);
563 if ((!fill && !segment->dur && !segment->end) ||
564 (fill && !strcmp (fill, "freeze")) ||
565 (fill && !strcmp (fill, "hold")))
566 freeze = TRUE;
568 } else if (last_one) {
569 LaughRoleTiming *seg =
570 (LaughRoleTiming *) g_slist_last (sub)->data;
571 last_one = seg->node->state >= LaughStateStopped;
573 if (found && !last_one)
574 break;
577 if (freeze) {
578 _laugh_timing_setting_stop_freeze (segment, LaughStateFreezed);
579 laugh_node_freeze (segment->node);
580 } else {
581 laugh_timing_setting_stop (segment);
584 if (start_next)
585 laugh_timing_setting_start (start_next);
586 else if (last_one &&
587 (!parent->dur ||
588 (parent->dur->type == LaughTimingTime && !parent->dur->handler_id)))
589 _laugh_timing_setting_stopped (parent);
590 else if (freeze)
591 g_printf ("freeze %s\n", laugh_tag_from_id (segment->node->id));
593 } else {
594 laugh_timing_setting_stop (segment);
598 static void _laugh_timing_element_synced (LaughNode *node, LaughRoleTiming *timing_role)
600 LaughTiming *timing = NULL;
602 if (timing_role->begin && timing_role->begin->handler_id)
603 timing = timing_role->begin;
604 else if (timing_role->end && timing_role->end->handler_id)
605 timing = timing_role->end;
607 if (!timing) {
608 g_printerr ("spurious element started event\n");
609 } else {
610 _laugh_timing_source_disconnect (timing);
612 _laugh_timing_time_connect (timing, timing->offset, timing_role);
616 static void
617 _laugh_timing_role_sync_on (LaughRoleTiming *role, LaughTiming *timing)
619 LaughNode *target;
620 LaughRoleTiming *target_role;
621 LaughTimingType type = timing->type;
622 LaughNodeState required_state;
623 const gchar *signal;
624 glong offset;
626 if (!timing->element_id) {
627 g_printerr ("Sync on element not specified\n");
628 return ;
631 target = laugh_document_get_element_by_id (role->node->document,
632 laugh_document_id_from_string (role->node->document, timing->element_id));
633 if (!target) {
634 g_printerr ("Sync on element %s: not found\n", timing->element_id);
635 return ;
638 target_role = (LaughRoleTiming *) laugh_node_role_get (target, LaughRoleTypeTiming);
639 if (!target_role) {
640 g_printerr ("Sync on element %s: has no timing\n", timing->element_id);
641 return ;
644 switch (type) {
645 case LaughTimingStartSync:
646 signal = "started";
647 required_state = LaughStateBegun;
648 offset = (clutter_get_timestamp () - target_role->start_time_stamp) / 1000;
649 break;
650 case LaughTimingEndSync:
651 signal = "stopped";
652 required_state = LaughStateStopped;
653 offset = (clutter_get_timestamp () - target_role->stop_time_stamp) / 1000;
654 break;
655 default:
656 g_printerr ("TODO: connection to element event %d\n", type);
657 return;
660 if (target->state >= required_state) {
661 _laugh_timing_time_connect (timing, timing->offset - offset, role);
662 } else {
663 _laugh_timing_source_connect (timing, target, signal,
664 (GCallback)_laugh_timing_element_synced, role);
668 static void _laugh_timing_setting_started (LaughRoleTiming *timing_role)
670 LaughTiming *dur = timing_role->dur;
671 GSList *s;
673 g_printf ("laugh_timing_setting_started %s\n",
674 laugh_tag_from_id (timing_role->node->id));
675 laugh_node_start (timing_role->node);
676 if (dur && LaughTimingMedia == dur->type)
677 g_printf ("laugh_timing_setting_started LaughTimingMedia\n");
679 if (dur && LaughTimingTime == dur->type)
680 _laugh_timing_time_connect (dur, dur->offset, timing_role);
681 else if (timing_role->end)
682 _laugh_timing_role_sync_on (timing_role, timing_role->end);
684 if (timing_role->active) {
685 if (timing_role->sub_segments) {
686 for (s = timing_role->sub_segments; s && timing_role->active; s = s->next)
687 laugh_timing_setting_start (
688 (LaughRoleTiming *)((GSList *) s->data)->data);
689 } else if (!timing_role->dur && !timing_role->end) {
690 _laugh_timing_setting_stopped (timing_role);
695 static gboolean _laugh_timing_timeout (gpointer data)
697 LaughRoleTiming *segment = (LaughRoleTiming *) data;
699 if (segment->begin && segment->begin->handler_id) {
700 laugh_timing_notify (segment, segment->begin->handler_id);
701 segment->begin->handler_id = 0;
702 } else if (segment->dur && segment->dur->handler_id) {
703 laugh_timing_notify (segment, segment->dur->handler_id);
704 segment->dur->handler_id = 0;
707 return FALSE; /*TODO handle repeat*/
710 gboolean laugh_timing_setting_start (LaughRoleTiming *timing_role)
712 LaughTimingType timing_type = LaughTimingTime;
713 int offset = 0;
715 g_printf ("laugh_timing_setting_start %s\n",
716 laugh_tag_from_id (timing_role->node->id));
717 timing_role->active = TRUE;
718 timing_role->start_time_stamp = clutter_get_timestamp ();
720 if (!timing_role->begin)
721 timing_role->begin = laugh_timing_new ();
723 timing_type = timing_role->begin->type;
724 offset = timing_role->begin->offset;
726 if (LaughTimingTime == timing_type)
727 _laugh_timing_time_connect (timing_role->begin, offset, timing_role);
728 else
729 _laugh_timing_role_sync_on (timing_role, timing_role->begin);
731 return TRUE;
734 void laugh_timing_notify (LaughRoleTiming *segment, gulong handler_id)
736 if (segment->begin && segment->begin->handler_id == handler_id) {
737 _laugh_timing_setting_started (segment);
738 } else if (segment->dur && segment->dur->handler_id == handler_id) {
739 _laugh_timing_setting_stopped (segment);
743 void laugh_timing_setting_stop (LaughRoleTiming *segment)
745 g_printf ("laugh_timing_setting_stop %s\n",
746 laugh_tag_from_id (segment->node->id));
748 _laugh_timing_setting_stop_freeze (segment, LaughStateStopped);
750 laugh_node_stop (segment->node);
753 static LaughRoleTiming *
754 _laugh_timing_settting_find (LaughRoleTiming *segment, const LaughNode *node)
756 if (segment->node == node)
757 return segment;
758 if (segment->sub_segments) {
759 GSList *s;
760 for (s = segment->sub_segments; s; s = s->next) {
761 GSList *sub;
762 for (sub = (GSList *) s->data; sub; sub = sub->next) {
763 LaughRoleTiming *seg = _laugh_timing_settting_find (
764 (LaughRoleTiming *) sub->data, node);
765 if (seg)
766 return seg;
770 return NULL;
773 static void _laugh_timing_settting_sub_segments_force_stop (LaughRoleTiming *segment)
775 GSList *s;
777 for (s = segment->sub_segments; s; s = s->next) {
778 GSList *sub;
779 for (sub = (GSList *) s->data; sub; sub = sub->next) {
780 LaughRoleTiming *seg = (LaughRoleTiming *) sub->data;
781 if (seg->node->state > LaughStateInitialized) {
782 _laugh_timing_setting_stop_freeze (seg, LaughStateInitialized);
783 if (seg->node->state > LaughStateInitialized &&
784 seg->node->state != LaughStateStopped) {
785 LaughNodeClass *klass = LAUGH_NODE_GET_CLASS(seg->node);
786 if (klass->stop)
787 klass->stop (seg->node);
789 seg->node->state = LaughStateInitialized;
790 } else {
791 break;
797 static void
798 _laugh_timing_settting_recursive_start (LaughRoleTiming *role, LaughRoleTiming *child)
800 if (!role) {
801 g_printerr ("jump target starting underflow error\n");
802 return;
804 if (child && LaughTagIdPar != role->node->id) {
805 GSList *s;
807 for (s = role->sub_segments; s; s = s->next) {
808 GSList *seg;
809 if (g_slist_find ((GSList *)s->data, child)) {
810 for (seg = (GSList *)s->data; seg; seg = seg->next) {
811 LaughRoleTiming *r = (LaughRoleTiming *)seg->data;
812 if (r == child)
813 break;
814 r->node->state = LaughStateStopped;
816 break;
821 if (!role->active) {
822 _laugh_timing_settting_recursive_start (role->parent, role);
823 if (LaughStateInitialized >= role->node->state)
824 laugh_timing_setting_start (role);
828 void laugh_timing_setting_jump (LaughNode *target)
830 LaughRoleTiming *segment = target->document->timing;
831 LaughRoleTiming *seg;
833 if (LaughStateBegun == target->state) {
834 g_printerr ("jump target already active\n");
835 return;
838 segment = _laugh_timing_settting_find (segment, target);
839 if (!segment) {
840 g_printerr ("jump target not in tree\n");
841 return;
844 for (seg = segment->parent; seg; seg = seg->parent)
845 if (seg->active) {
846 g_printf ("laugh_timing_setting_jump force stop %s\n",
847 laugh_tag_from_id (seg->node->id));
849 if (LaughTagIdPar != seg->node->id)
850 _laugh_timing_settting_sub_segments_force_stop (seg);
851 _laugh_timing_settting_recursive_start (segment, NULL);
853 break;
857 LaughNode *laugh_timing_container_new (LaughDocument *doc, LaughNodeTagId id,
858 GHashTable *attributes)
860 LaughNode *n = (LaughNode *)g_object_new(LAUGH_TYPE_TIMING_CONTAINER, NULL);
862 laugh_node_base_construct (doc, n, id, attributes);
864 return n;
867 int laugh_timing_set_value (LaughTiming *timing, const gchar *value)
869 return _get_timing (value, timing);
872 /*#define TIMING_TEST*/
874 #ifdef TIMING_TEST
876 /* gcc laugh-timing.c -o timing-test `pkg-config --cflags --libs glib-2.0` -DTIMING_TEST*/
878 #define TEST_PARSE_TIME(s) \
880 int val = 0; \
881 int b = _timing_parse_time(s, &val); \
882 g_printf( "_timing_parse_time(%s) => %d: %d\n", (s), b, val); \
885 const char *timing_string (LaughTimingType tt) {
886 switch (tt) {
887 case LaughTimingUnknown:
888 return "unknown";
889 case LaughTimingTime:
890 return "timer";
891 case LaughTimingIndefinite:
892 return "infinite";
893 case LaughTimingMedia:
894 return "media";
895 case LaughTimingActivated:
896 return "activated";
897 case LaughTimingInbounds:
898 return "inbounds";
899 case LaughTimingOutbounds:
900 return "outbounds";
901 case LaughTimingEndSync:
902 return "endSync";
903 case LaughTimingStartSync:
904 return "startSync";
905 default:
906 break;
908 return "error";
911 #define TEST_GET_TIMING(s) \
913 LaughTiming *t = laugh_timing_new_from_string(s); \
914 g_printf( "_get_timing %s => target %s event %s offset%d\n", \
915 (s), t->element_id, timing_string(t->type), t->offset); \
916 laugh_timing_delete (t); \
919 int main () {
920 TEST_PARSE_TIME("12");
921 TEST_PARSE_TIME("12s");
922 TEST_PARSE_TIME("12m");
923 TEST_PARSE_TIME("12h");
924 TEST_PARSE_TIME("12.4");
925 TEST_PARSE_TIME("12.4s");
926 TEST_PARSE_TIME("12.4m");
927 TEST_PARSE_TIME("12.4h");
928 TEST_PARSE_TIME(".4");
929 TEST_PARSE_TIME(".4s");
930 TEST_PARSE_TIME("+.4");
931 TEST_PARSE_TIME("+.4s");
932 TEST_PARSE_TIME("-.4");
933 TEST_PARSE_TIME("-.4s");
934 TEST_GET_TIMING("4");
935 TEST_GET_TIMING("myimage.activateEvent");
936 TEST_GET_TIMING("myimage.activateEvent+2");
937 TEST_GET_TIMING("myimage.endSync");
938 TEST_GET_TIMING("myimage.endSync-2m");
939 TEST_GET_TIMING("MyImage.endSync");
940 TEST_GET_TIMING("id(MyImage)(endSync)");
941 TEST_GET_TIMING("indefinite");
942 return 0;
945 #endif