This source file includes following definitions.
- gf_dom_refresh_event_filter
- gf_sg_unregister_event_type
- gf_sg_register_event_type
- gf_sg_get_dom_event_filter
- gf_node_get_dom_event_filter
- gf_sg_listener_add
- gf_node_dom_listener_add
- gf_dom_listener_del
- gf_dom_event_remove_listener_from_parent
- gf_dom_event_remove_all_listeners
- gf_dom_listener_count
- gf_dom_listener_get
- gf_sg_listener_post_add
- gf_dom_listener_process_add
- gf_dom_listener_reset_defered
- gf_sg_handle_dom_event
- dom_evt_get_handler
- dom_event_process
- gf_sg_fire_dom_event
- gf_sg_dom_event_bubble
- gf_sg_dom_stack_parents
- gf_dom_event_fire_ex
- gf_dom_event_fire
- gf_dom_listener_build_ex
- gf_dom_listener_build
- gf_smil_handle_event
- gf_smil_handle_event_begin
- gf_smil_handle_event_end
- gf_smil_setup_event_list
- gf_smil_setup_events
- gf_dom_set_textContent
- gf_dom_add_text_node
- gf_dom_new_text_node
- gf_dom_flatten_textContent
- gf_dom_add_updates_node
- gf_dom_add_update_node
- gf_dom_event_dump_listeners
- gf_dom_event_target_new
- gf_dom_event_target_del
- gf_dom_event_get_target_from_node
#include <gpac/internal/scenegraph_dev.h>
#include <gpac/xml.h>
#ifndef GPAC_DISABLE_SVG
#include <gpac/scenegraph_svg.h>
#include <gpac/events.h>
#include <gpac/nodes_svg.h>
static void gf_smil_handle_event(GF_Node *anim, GF_FieldInfo *info, GF_DOM_Event *evt, Bool is_end);
static void gf_dom_refresh_event_filter(GF_SceneGraph *sg)
{
GF_SceneGraph *par;
u32 prev_flags = sg->dom_evt_filter;
sg->dom_evt_filter = 0;
if (sg->nb_evts_mouse) sg->dom_evt_filter |= GF_DOM_EVENT_MOUSE;
if (sg->nb_evts_focus) sg->dom_evt_filter |= GF_DOM_EVENT_FOCUS;
if (sg->nb_evts_key) sg->dom_evt_filter |= GF_DOM_EVENT_KEY;
if (sg->nb_evts_ui) sg->dom_evt_filter |= GF_DOM_EVENT_UI;
if (sg->nb_evts_mutation) sg->dom_evt_filter |= GF_DOM_EVENT_MUTATION;
if (sg->nb_evts_text) sg->dom_evt_filter |= GF_DOM_EVENT_TEXT;
if (sg->nb_evts_smil) sg->dom_evt_filter |= GF_DOM_EVENT_SMIL;
if (sg->nb_evts_laser) sg->dom_evt_filter |= GF_DOM_EVENT_LASER;
if (sg->nb_evts_svg) sg->dom_evt_filter |= GF_DOM_EVENT_SVG;
if (sg->nb_evts_media) sg->dom_evt_filter |= GF_DOM_EVENT_MEDIA;
par = sg->parent_scene;
while (par) {
par->dom_evt_filter &= ~prev_flags;
par->dom_evt_filter |= sg->dom_evt_filter;
par = par->parent_scene;
}
}
void gf_sg_unregister_event_type(GF_SceneGraph *sg, GF_DOMEventCategory category)
{
if (sg->nb_evts_mouse && (category & GF_DOM_EVENT_MOUSE)) sg->nb_evts_mouse--;
if (sg->nb_evts_focus && (category & GF_DOM_EVENT_FOCUS)) sg->nb_evts_focus--;
if (sg->nb_evts_key && (category & GF_DOM_EVENT_KEY)) sg->nb_evts_key--;
if (sg->nb_evts_ui && (category & GF_DOM_EVENT_UI)) sg->nb_evts_ui--;
if (sg->nb_evts_mutation && (category & GF_DOM_EVENT_MUTATION)) sg->nb_evts_mutation--;
if (sg->nb_evts_text && (category & GF_DOM_EVENT_TEXT)) sg->nb_evts_text--;
if (sg->nb_evts_smil && (category & GF_DOM_EVENT_SMIL)) sg->nb_evts_smil--;
if (sg->nb_evts_laser && (category & GF_DOM_EVENT_LASER)) sg->nb_evts_laser--;
if (sg->nb_evts_text && (category & GF_DOM_EVENT_TEXT)) sg->nb_evts_text--;
if (sg->nb_evts_svg && (category & GF_DOM_EVENT_SVG)) sg->nb_evts_svg--;
if (sg->nb_evts_media && (category & GF_DOM_EVENT_MEDIA)) sg->nb_evts_media--;
gf_dom_refresh_event_filter(sg);
}
void gf_sg_register_event_type(GF_SceneGraph *sg, GF_DOMEventCategory category)
{
if (category & GF_DOM_EVENT_MOUSE) sg->nb_evts_mouse++;
if (category & GF_DOM_EVENT_FOCUS) sg->nb_evts_focus++;
if (category & GF_DOM_EVENT_KEY) sg->nb_evts_key++;
if (category & GF_DOM_EVENT_UI) sg->nb_evts_ui++;
if (category & GF_DOM_EVENT_MUTATION) sg->nb_evts_mutation++;
if (category & GF_DOM_EVENT_TEXT) sg->nb_evts_text++;
if (category & GF_DOM_EVENT_SMIL) sg->nb_evts_smil++;
if (category & GF_DOM_EVENT_LASER) sg->nb_evts_laser++;
if (category & GF_DOM_EVENT_SVG) sg->nb_evts_svg++;
if (category & GF_DOM_EVENT_MEDIA) sg->nb_evts_media++;
gf_dom_refresh_event_filter(sg);
}
u32 gf_sg_get_dom_event_filter(GF_SceneGraph *sg)
{
return sg->dom_evt_filter;
}
u32 gf_node_get_dom_event_filter(GF_Node *node)
{
if (node) {
return node->sgprivate->scenegraph->dom_evt_filter;
} else {
return 0;
}
}
GF_Err gf_sg_listener_add(GF_Node *listener, GF_DOMEventTarget *evt_target)
{
GF_FieldInfo info;
if (!evt_target || !listener) return GF_BAD_PARAM;
if (listener->sgprivate->tag!=TAG_SVG_listener)
return GF_BAD_PARAM;
if (listener->sgprivate->UserPrivate!=NULL) return GF_NOT_SUPPORTED;
listener->sgprivate->UserPrivate = evt_target;
gf_node_register((GF_Node *)listener, NULL);
if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info) == GF_OK) {
GF_EventType type = ((XMLEV_Event *)info.far_ptr)->type;
gf_sg_register_event_type(listener->sgprivate->scenegraph, gf_dom_event_get_category(type));
}
return gf_list_add(evt_target->listeners, listener);
}
GF_EXPORT
GF_Err gf_node_dom_listener_add(GF_Node *node, GF_Node *listener)
{
if (!node || !listener) return GF_BAD_PARAM;
if (listener->sgprivate->tag!=TAG_SVG_listener)
return GF_BAD_PARAM;
if (!node->sgprivate->interact) {
GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext);
if (!node->sgprivate->interact)
return GF_OUT_OF_MEM;
}
if (!node->sgprivate->interact->dom_evt) {
node->sgprivate->interact->dom_evt = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_NODE, node);
}
return gf_sg_listener_add(listener, node->sgprivate->interact->dom_evt);
}
GF_EXPORT
GF_Err gf_dom_listener_del(GF_Node *listener, GF_DOMEventTarget *target)
{
GF_FieldInfo info;
if (!listener || !target) return GF_BAD_PARAM;
if (gf_list_del_item(target->listeners, listener)<0) return GF_BAD_PARAM;
if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info) == GF_OK) {
GF_EventType type = ((XMLEV_Event *)info.far_ptr)->type;
gf_sg_unregister_event_type(listener->sgprivate->scenegraph, gf_dom_event_get_category(type));
}
listener->sgprivate->UserPrivate = NULL;
gf_node_unregister((GF_Node *)listener, NULL);
return GF_OK;
}
GF_Err gf_dom_event_remove_listener_from_parent(GF_DOMEventTarget *event_target, GF_Node *listener)
{
if (!event_target) return GF_BAD_PARAM;
if (event_target->ptr_type == GF_DOM_EVENT_TARGET_NODE) {
GF_Node *node = (GF_Node *)event_target->ptr;
node->sgprivate->UserPrivate = NULL;
}
gf_list_del_item(event_target->listeners, listener);
#if 0
{
GF_FieldInfo info;
if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info) == GF_OK) {
GF_EventType type = ((XMLEV_Event *)info.far_ptr)->type;
gf_sg_unregister_event_type(listener->sgprivate->scenegraph, gf_dom_event_get_category(type));
}
}
#endif
return GF_OK;
}
void gf_dom_event_remove_all_listeners(GF_DOMEventTarget *event_target)
{
while (gf_list_count(event_target->listeners)) {
GF_Node *n = (GF_Node *)gf_list_get(event_target->listeners, 0);
gf_dom_listener_del(n, event_target);
}
}
GF_EXPORT
u32 gf_dom_listener_count(GF_Node *node)
{
if (!node || !node->sgprivate->interact || !node->sgprivate->interact->dom_evt) return 0;
return gf_list_count(node->sgprivate->interact->dom_evt->listeners);
}
GF_EXPORT
GF_Node *gf_dom_listener_get(GF_Node *node, u32 i)
{
if (!node || !node->sgprivate->interact || !node->sgprivate->interact->dom_evt) return 0;
return (GF_Node *)gf_list_get(node->sgprivate->interact->dom_evt->listeners, i);
}
typedef struct
{
GF_Node *obs;
GF_Node *listener;
} DOMAddListener;
void gf_sg_listener_post_add(GF_Node *obs, GF_Node *listener)
{
DOMAddListener *l;
gf_mx_p(obs->sgprivate->scenegraph->dom_evt_mx);
l = (DOMAddListener*)gf_malloc(sizeof(DOMAddListener));
l->listener = listener;
l->obs = obs;
gf_list_add(obs->sgprivate->scenegraph->listeners_to_add, l);
gf_mx_v(obs->sgprivate->scenegraph->dom_evt_mx);
}
void gf_dom_listener_process_add(GF_SceneGraph *sg)
{
u32 i, count;
gf_mx_p(sg->dom_evt_mx);
count = gf_list_count(sg->listeners_to_add);
for (i=0; i<count; i++) {
DOMAddListener *l = (DOMAddListener *)gf_list_get(sg->listeners_to_add, i);
gf_node_dom_listener_add(l->obs, l->listener);
gf_free(l);
}
gf_list_reset(sg->listeners_to_add);
gf_mx_v(sg->dom_evt_mx);
}
void gf_dom_listener_reset_defered(GF_SceneGraph *sg)
{
gf_mx_p(sg->dom_evt_mx);
while (gf_list_count(sg->listeners_to_add)) {
DOMAddListener *l = (DOMAddListener *)gf_list_get(sg->listeners_to_add, 0);
gf_list_rem(sg->listeners_to_add, 0);
gf_free(l);
}
gf_mx_v(sg->dom_evt_mx);
}
void gf_sg_handle_dom_event(GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer)
{
#ifdef GPAC_HAS_SPIDERMONKEY
if (hdl->sgprivate->scenegraph->svg_js) {
if (hdl->sgprivate->scenegraph->svg_js->handler_execute(hdl, event, observer, NULL)) {
return;
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[DOM Events] Error executing JavaScript event handler\n"));
return;
}
}
#endif
GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[DOM Events] JavaScript context not found \n"));
}
static GF_Node *dom_evt_get_handler(GF_Node *n)
{
XMLRI *iri;
GF_FieldInfo info;
if (!n || (n->sgprivate->tag!=TAG_SVG_handler)) return n;
if (gf_node_get_attribute_by_tag(n, TAG_XLINK_ATT_href, GF_FALSE, GF_FALSE, &info) != GF_OK) {
return n;
}
iri = (XMLRI *)info.far_ptr;
if (!iri->target && iri->string) {
iri->target = gf_sg_find_node_by_name(n->sgprivate->scenegraph, iri->string+1);
}
return dom_evt_get_handler((GF_Node *)iri->target);
}
static void dom_event_process(GF_Node *listen, GF_DOM_Event *event, GF_Node *observer)
{
GF_Node *hdl_node;
switch (listen->sgprivate->tag) {
case TAG_SVG_listener:
{
GF_FieldInfo info;
if (gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info) == GF_OK) {
XMLRI *iri = (XMLRI *)info.far_ptr;
if ((iri->type==XMLRI_STRING) && iri->string && !strnicmp(iri->string, "javascript:", 11)) {
#ifdef GPAC_HAS_SPIDERMONKEY
if (listen->sgprivate->scenegraph->svg_js)
listen->sgprivate->scenegraph->svg_js->handler_execute(listen, event, observer, iri->string + 11);
#endif
return;
}
if (!iri->target && iri->string) {
iri->target = gf_sg_find_node_by_name(listen->sgprivate->scenegraph, iri->string+1);
}
hdl_node = dom_evt_get_handler((GF_Node *)iri->target);
} else {
return;
}
}
break;
default:
return;
}
if (!hdl_node) return;
GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events ] Time %f - Processing event type: %s\n", gf_node_get_scene_time((GF_Node *)listen), gf_dom_event_get_name(event->type)));
switch (hdl_node->sgprivate->tag) {
case TAG_SVG_handler:
{
SVG_handlerElement *handler = (SVG_handlerElement *) hdl_node;
if (!handler->handle_event) return;
#if 0
if (gf_node_get_attribute_by_tag(hdl_node, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info) == GF_OK) {
XMLEV_Event *ev_event = (XMLEV_Event *)info.far_ptr;
if (ev_event->type != event->type) return;
handler->handle_event(hdl_node, event, observer);
} else {
handler->handle_event(hdl_node, event, observer);
}
#else
handler->handle_event(hdl_node, event, observer);
#endif
}
break;
case TAG_LSR_conditional:
if ( ((SVG_Element*)hdl_node)->children)
gf_node_traverse(((SVG_Element*)hdl_node)->children->node, NULL);
break;
case TAG_SVG_a:
{
GF_DOM_Event act;
memset(&act, 0, sizeof(GF_DOM_Event));
act.type = GF_EVENT_ACTIVATE;
gf_dom_event_fire((GF_Node *)hdl_node, &act);
}
break;
default:
return;
}
}
GF_EXPORT
Bool gf_sg_fire_dom_event(GF_DOMEventTarget *et, GF_DOM_Event *event, GF_SceneGraph *sg, GF_Node *n)
{
if (et) {
if (et->ptr_type==GF_DOM_EVENT_TARGET_NODE ||
et->ptr_type == GF_DOM_EVENT_TARGET_DOCUMENT ||
et->ptr_type == GF_DOM_EVENT_TARGET_XHR ||
et->ptr_type == GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE ||
et->ptr_type == GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER ||
et->ptr_type == GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST ) {
GF_Node *observer = NULL;
u32 i, count, post_count;
if (et->ptr_type==GF_DOM_EVENT_TARGET_NODE) {
observer = (GF_Node *)et->ptr;
}
count = gf_list_count(et->listeners);
for (i=0; i<count; i++) {
XMLEV_Event *listened_event;
GF_Node *listen = (GF_Node *)gf_list_get(et->listeners, i);
switch (listen->sgprivate->tag) {
case TAG_SVG_listener:
{
SVGAllAttributes atts;
gf_svg_flatten_attributes((SVG_Element*)listen, &atts);
listened_event = atts.event;
if (!listened_event) continue;
if (atts.propagate && (*atts.propagate==XMLEVENT_PROPAGATE_STOP))
event->event_phase |= GF_DOM_EVENT_PHASE_CANCEL;
if (atts.defaultAction && (*atts.defaultAction==XMLEVENT_DEFAULTACTION_CANCEL))
event->event_phase |= GF_DOM_EVENT_PHASE_PREVENT;
}
break;
default:
continue;
}
if (listened_event->type <= GF_EVENT_MOUSEMOVE) event->has_ui_events=1;
if (listened_event->type != event->type) continue;
if (listened_event->parameter && (listened_event->parameter != event->detail)) continue;
event->currentTarget = et;
event->consumed ++;
if (event->type==GF_EVENT_LOAD) {
dom_event_process(listen, event, observer);
} else if (n) {
assert(n->sgprivate->num_instances);
n->sgprivate->num_instances++;
dom_event_process(listen, event, observer);
if (n->sgprivate->num_instances==1) {
gf_node_unregister(n, NULL);
return GF_FALSE;
}
n->sgprivate->num_instances--;
} else {
dom_event_process(listen, event, observer);
}
if (event->event_phase & GF_DOM_EVENT_PHASE_CANCEL_ALL) {
gf_dom_listener_process_add(sg);
return GF_FALSE;
}
post_count = gf_list_count(et->listeners);
if (post_count < count) {
s32 pos = gf_list_find(et->listeners, listen);
if (pos>=0) i = pos;
else i--;
count = post_count;
}
}
if (event->event_phase & (GF_DOM_EVENT_PHASE_CANCEL|GF_DOM_EVENT_PHASE_CANCEL_ALL) ) {
gf_dom_listener_process_add(sg);
return GF_FALSE;
}
}
gf_dom_listener_process_add(sg);
return n ? GF_TRUE : GF_FALSE;
} else {
return GF_TRUE;
}
}
static void gf_sg_dom_event_bubble(GF_Node *node, GF_DOM_Event *event, GF_List *use_stack, u32 cur_par_idx)
{
GF_Node *parent;
if (!node || node->sgprivate->scenegraph->abort_bubbling) return;
parent = gf_node_get_parent(node, 0);
if (!parent) {
if (node->sgprivate->scenegraph->RootNode==node)
gf_sg_fire_dom_event(node->sgprivate->scenegraph->dom_evt, event, node->sgprivate->scenegraph, NULL);
return;
}
if (cur_par_idx) {
GF_Node *used_node = (GF_Node *)gf_list_get(use_stack, cur_par_idx-1);
if (used_node==node) {
parent = (GF_Node *)gf_list_get(use_stack, cur_par_idx);
if (cur_par_idx>1) cur_par_idx-=2;
else cur_par_idx = 0;
if (parent->sgprivate->interact) {
Bool can_bubble = gf_sg_fire_dom_event(parent->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, parent);
if (!can_bubble) {
return;
}
}
gf_sg_dom_event_bubble(parent, event, use_stack, cur_par_idx);
return;
}
}
if (parent->sgprivate->interact) {
Bool can_bubble;
can_bubble = gf_sg_fire_dom_event(parent->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, parent);
if(!can_bubble) return;
}
gf_sg_dom_event_bubble(parent, event, use_stack, cur_par_idx);
}
void gf_sg_dom_stack_parents(GF_Node *node, GF_List *stack)
{
if (!node) return;
if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) gf_list_insert(stack, node, 0);
gf_sg_dom_stack_parents(gf_node_get_parent(node, 0), stack);
}
GF_EXPORT
Bool gf_dom_event_fire_ex(GF_Node *node, GF_DOM_Event *event, GF_List *use_stack)
{
GF_SceneGraph *sg;
GF_List *prev_use_stack;
Bool prev_bub;
GF_DOMEventTarget cur_target;
u32 cur_par_idx;
Bool can_bubble = GF_FALSE;
if (!node || !event) return GF_FALSE;
GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events ] Graph %p Time %f - Firing event %s.%s\n", gf_node_get_graph(node), gf_node_get_scene_time(node), gf_node_get_log_name(node), gf_dom_event_get_name(event->type)));
gf_dom_listener_process_add(node->sgprivate->scenegraph);
event->consumed = 0;
event->target = node;
event->target_type = GF_DOM_EVENT_TARGET_NODE;
if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) {
event->currentTarget = node->sgprivate->interact->dom_evt;
} else {
cur_target.ptr_type = GF_DOM_EVENT_TARGET_NODE;
cur_target.ptr = node;
cur_target.listeners = NULL;
event->currentTarget = &cur_target;
}
if (0) {
Bool aborted = GF_FALSE;
u32 i, count;
GF_List *parents;
event->event_phase = GF_DOM_EVENT_PHASE_CAPTURE;
parents = gf_list_new();
gf_sg_dom_stack_parents(gf_node_get_parent(node, 0), parents);
count = gf_list_count(parents);
for (i=0; i<count; i++) {
GF_Node *n = (GF_Node *)gf_list_get(parents, i);
if (n->sgprivate->interact)
gf_sg_fire_dom_event(n->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, n);
if (event->event_phase & (GF_DOM_EVENT_PHASE_CANCEL|GF_DOM_EVENT_PHASE_CANCEL_ALL) ) {
aborted = GF_TRUE;
break;
}
}
gf_list_del(parents);
if (aborted) return GF_TRUE;
}
event->event_phase = GF_DOM_EVENT_PHASE_AT_TARGET;
cur_par_idx = 0;
if (use_stack) {
cur_par_idx = gf_list_count(use_stack);
if (cur_par_idx) cur_par_idx--;
}
sg = node->sgprivate->scenegraph;
prev_use_stack = sg->use_stack ;
prev_bub = sg->abort_bubbling;
sg->use_stack = use_stack;
sg->abort_bubbling = GF_FALSE;
if (node->sgprivate->interact) {
can_bubble = gf_sg_fire_dom_event(node->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, node);
}
if ( (!node->sgprivate->interact || can_bubble) && event->bubbles) {
event->event_phase = GF_DOM_EVENT_PHASE_BUBBLE;
gf_sg_dom_event_bubble(node, event, use_stack, cur_par_idx);
}
sg->use_stack = prev_use_stack;
sg->abort_bubbling = prev_bub;
return event->consumed ? GF_TRUE : GF_FALSE;
}
GF_EXPORT
Bool gf_dom_event_fire(GF_Node *node, GF_DOM_Event *event)
{
return gf_dom_event_fire_ex(node, event, NULL);
}
GF_DOMHandler *gf_dom_listener_build_ex(GF_Node *node, u32 event_type, u32 event_parameter, GF_Node *handler, GF_Node **out_listener)
{
SVG_Element *listener;
GF_FieldInfo info;
GF_ChildNodeItem *last = NULL;
if (!node || !node->sgprivate || !node->sgprivate->scenegraph) {
GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[DOM Events] Bad target node for listener\n"));
return NULL;
}
listener = (SVG_Element *) gf_node_new(node->sgprivate->scenegraph, TAG_SVG_listener);
if (handler) {
if (gf_node_get_attribute_by_tag(handler, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info)==GF_OK) {
event_type = ((XMLEV_Event *)info.far_ptr)->type;
event_parameter = ((XMLEV_Event *)info.far_ptr)->parameter;
}
} else {
handler = gf_node_new(node->sgprivate->scenegraph, TAG_SVG_handler);
gf_node_get_attribute_by_tag(handler, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
((XMLEV_Event *)info.far_ptr)->type = event_type;
((XMLEV_Event *)info.far_ptr)->parameter = event_parameter;
gf_node_register((GF_Node *)handler, (GF_Node *) listener);
gf_node_list_add_child_last(& ((GF_ParentNode *)listener)->children, (GF_Node*)handler, &last);
}
gf_node_get_attribute_by_tag((GF_Node*)listener, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
((XMLEV_Event *)info.far_ptr)->type = event_type;
((XMLEV_Event *)info.far_ptr)->parameter = event_parameter;
gf_node_get_attribute_by_tag((GF_Node*)listener, TAG_XMLEV_ATT_handler, GF_TRUE, GF_FALSE, &info);
((XMLRI *)info.far_ptr)->target = handler;
gf_node_get_attribute_by_tag((GF_Node*)listener, TAG_XMLEV_ATT_target, GF_TRUE, GF_FALSE, &info);
((XMLRI *)info.far_ptr)->target = node;
gf_node_dom_listener_add((GF_Node *) node, (GF_Node *) listener);
if (out_listener) *out_listener = (GF_Node *) listener;
((SVG_handlerElement *) handler)->handle_event = gf_sg_handle_dom_event;
return (SVG_handlerElement *) handler;
}
GF_EXPORT
GF_DOMHandler *gf_dom_listener_build(GF_Node *node, GF_EventType event_type, u32 event_parameter)
{
return gf_dom_listener_build_ex(node, event_type, event_parameter, NULL, NULL);
}
static void gf_smil_handle_event(GF_Node *timed_elt, GF_FieldInfo *info, GF_DOM_Event *evt, Bool is_end)
{
SMIL_Time *resolved, *proto;
Double scene_time = gf_node_get_scene_time((GF_Node *)evt->target);
GF_List *times = *(GF_List **)info->far_ptr;
u32 found = GF_FALSE;
u32 i, j, count = gf_list_count(times);
for (i=0; i<count; i++) {
proto = (SMIL_Time*)gf_list_get(times, i);
if ((proto->type == GF_SMIL_TIME_EVENT_RESOLVED) && (proto->clock<scene_time) ) {
gf_free(proto);
gf_list_rem(times, i);
i--;
count--;
}
}
for (i=0; i<count; i++) {
proto = (SMIL_Time*)gf_list_get(times, i);
if (proto->type != GF_SMIL_TIME_EVENT) continue;
if (proto->event.type != evt->type) continue;
if ((evt->type == GF_EVENT_KEYDOWN) || (evt->type == GF_EVENT_REPEAT_EVENT)) {
if (proto->event.parameter!=evt->detail) continue;
}
if (proto->element) {
if ((evt->currentTarget->ptr_type!=GF_DOM_EVENT_TARGET_NODE) || (proto->element!= (GF_Node*)evt->currentTarget->ptr))
continue;
}
GF_SAFEALLOC(resolved, SMIL_Time);
if (!resolved) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] Failed to allocate SMIL timing resolved value\n"));
continue;
}
resolved->type = GF_SMIL_TIME_EVENT_RESOLVED;
if (proto->is_absolute_event) {
resolved->clock = evt->smil_event_time + proto->clock;
} else {
resolved->clock = scene_time + proto->clock;
}
for (j=0; j<count; j++) {
proto = (SMIL_Time*)gf_list_get(times, j);
if ( GF_SMIL_TIME_IS_CLOCK(proto->type) ) {
if (proto->clock > resolved->clock) break;
} else {
break;
}
}
gf_list_insert(times, resolved, j);
if (j!=count) i++;
count++;
found++;
GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Inserting new time in %s: %f\n", gf_node_get_scene_time(timed_elt), gf_node_get_log_name(timed_elt), (is_end?"end":"begin"), resolved->clock));
}
if (found) gf_node_changed(timed_elt, info);
}
static void gf_smil_handle_event_begin(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer)
{
GF_FieldInfo info;
GF_Node *timed_elt = (GF_Node *)gf_node_get_private(hdl);
memset(&info, 0, sizeof(GF_FieldInfo));
info.name = "begin";
info.far_ptr = ((SVGTimedAnimBaseElement *)timed_elt)->timingp->begin;
info.fieldType = SMIL_Times_datatype;
gf_smil_handle_event(timed_elt, &info, evt, GF_FALSE);
}
static void gf_smil_handle_event_end(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer)
{
GF_FieldInfo info;
GF_Node *timed_elt = (GF_Node *)gf_node_get_private(hdl);
memset(&info, 0, sizeof(GF_FieldInfo));
info.name = "end";
info.far_ptr = ((SVGTimedAnimBaseElement *)timed_elt)->timingp->end;
info.fieldType = SMIL_Times_datatype;
gf_smil_handle_event((GF_Node *)timed_elt, &info, evt, GF_TRUE);
}
static void gf_smil_setup_event_list(GF_Node *node, GF_List *l, Bool is_begin)
{
void *hdl;
u32 i, count;
count = gf_list_count(l);
for (i=0; i<count; i++) {
SMIL_Time *t = (SMIL_Time*)gf_list_get(l, i);
if (t->type != GF_SMIL_TIME_EVENT) continue;
if (!t->element && t->element_id) continue;
if (t->event.type==GF_EVENT_BEGIN) {
t->event.type=GF_EVENT_BEGIN_EVENT;
t->is_absolute_event = GF_TRUE;
} else if (t->event.type==GF_EVENT_END) {
t->event.type=GF_EVENT_END_EVENT;
t->is_absolute_event = GF_TRUE;
} else if (t->event.type==GF_EVENT_REPEAT) {
t->event.type=GF_EVENT_REPEAT_EVENT;
t->is_absolute_event = GF_TRUE;
}
hdl = gf_dom_listener_build_ex(t->element, t->event.type, t->event.parameter, NULL, &t->listener);
if (t->listener)
gf_node_register(t->listener, NULL);
if (hdl) {
((SVG_handlerElement *)hdl)->handle_event = is_begin ? gf_smil_handle_event_begin : gf_smil_handle_event_end;
}
else {
continue;
}
gf_node_set_private((GF_Node *)hdl, node);
gf_node_register((GF_Node*)node, NULL);
}
}
void gf_smil_setup_events(GF_Node *node)
{
GF_FieldInfo info;
if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_begin, GF_FALSE, GF_FALSE, &info) == GF_OK)
gf_smil_setup_event_list(node, * (GF_List **)info.far_ptr, GF_TRUE);
if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_end, GF_FALSE, GF_FALSE, &info) == GF_OK)
gf_smil_setup_event_list(node, * (GF_List **)info.far_ptr, GF_FALSE);
}
GF_EXPORT
void gf_dom_set_textContent(GF_Node *n, char *text)
{
GF_ParentNode *par = (GF_ParentNode *)n;
gf_node_unregister_children(n, par->children);
par->children = NULL;
if (text) gf_dom_add_text_node(n, gf_strdup( text) );
}
GF_EXPORT
GF_DOMText *gf_dom_add_text_node(GF_Node *parent, char *text_data)
{
GF_DOMText *text;
GF_SAFEALLOC(text, GF_DOMText);
if (!text) return NULL;
gf_node_setup((GF_Node *)text, TAG_DOMText);
text->sgprivate->scenegraph = parent->sgprivate->scenegraph;
text->textContent = text_data;
gf_node_register((GF_Node *)text, parent);
gf_node_list_add_child_last(&((GF_ParentNode *)parent)->children, (GF_Node*)text, NULL);
return text;
}
GF_DOMText *gf_dom_new_text_node(GF_SceneGraph *sg)
{
GF_DOMText *text;
GF_SAFEALLOC(text, GF_DOMText);
if (!text) return NULL;
gf_node_setup((GF_Node *)text, TAG_DOMText);
text->sgprivate->scenegraph = sg;
return text;
}
GF_EXPORT
char *gf_dom_flatten_textContent(GF_Node *n)
{
u32 len = 0;
char *res = NULL;
GF_ChildNodeItem *list;
if ((n->sgprivate->tag==TAG_DOMText) && ((GF_DOMText*)n)->textContent) {
{
res = gf_strdup(((GF_DOMText*)n)->textContent);
len = (u32) strlen(res);
}
}
list = ((GF_ParentNode *)n)->children;
while (list) {
char *t = gf_dom_flatten_textContent(list->node);
if (t) {
size_t sub_len = strlen(t);
res = (char *)gf_realloc(res, sizeof(char)*(len+sub_len+1));
if (!len) res[0] = 0;
len += (u32)sub_len;
strcat(res, t);
gf_free(t);
}
list = list->next;
}
return res;
}
GF_DOMUpdates *gf_dom_add_updates_node(GF_Node *parent)
{
GF_DOMUpdates *text;
GF_SAFEALLOC(text, GF_DOMUpdates);
if (!text) return NULL;
gf_node_setup((GF_Node *)text, TAG_DOMUpdates);
text->sgprivate->scenegraph = parent->sgprivate->scenegraph;
text->updates = gf_list_new();
gf_node_register((GF_Node *)text, parent);
gf_node_list_add_child_last(&((GF_ParentNode *)parent)->children, (GF_Node*)text, NULL);
return text;
}
GF_DOMUpdates *gf_dom_add_update_node(GF_Node *parent)
{
GF_DOMUpdates *update;
GF_SAFEALLOC(update, GF_DOMUpdates);
if (!update) return NULL;
gf_node_setup((GF_Node *)update, TAG_DOMUpdates);
update->sgprivate->scenegraph = parent->sgprivate->scenegraph;
update->updates = gf_list_new();
gf_node_register((GF_Node *)update, parent);
gf_node_list_add_child_last(&((GF_ParentNode *)parent)->children, (GF_Node*)update, NULL);
return update;
}
void gf_dom_event_dump_listeners(GF_Node *n, FILE *f)
{
u32 i;
u32 count;
GF_List *listeners = NULL;
GF_FieldInfo info;
if (n && n->sgprivate && n->sgprivate->interact && n->sgprivate->interact->dom_evt) {
listeners = n->sgprivate->interact->dom_evt->listeners;
count = gf_list_count(listeners);
for (i=0; i<count; i++) {
SVG_handlerElement *hdl;
GF_Node *listener = (GF_Node *)gf_list_get(listeners, i);
if (listener->sgprivate->parents) continue;
if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info)==GF_OK) {
GF_DOMText *txt;
hdl = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target;
if (!hdl) continue;
if (hdl->sgprivate->parents
&& (hdl->sgprivate->parents->next || (hdl->sgprivate->parents->node != listener))
)
continue;
txt = hdl->children ? (GF_DOMText*)hdl->children->node : NULL;
if (!txt || (txt->sgprivate->tag!=TAG_DOMText) || !txt->textContent) continue;
if (gf_node_get_attribute_by_tag((GF_Node*)hdl, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info)==GF_OK) {
fprintf(f, " on%s=\"%s\"", gf_dom_event_get_name( ((XMLEV_Event*)info.far_ptr)->type), txt->textContent);
}
}
}
}
}
GF_DOMEventTarget *gf_dom_event_target_new(GF_DOMEventTargetType type, void *obj)
{
GF_DOMEventTarget *target;
GF_SAFEALLOC(target, GF_DOMEventTarget);
if (!target) return NULL;
target->ptr_type = type;
target->listeners = gf_list_new();
target->ptr = obj;
return target;
}
void gf_dom_event_target_del(GF_DOMEventTarget *target)
{
assert(gf_list_count(target->listeners) == 0);
gf_list_del(target->listeners);
gf_free(target);
}
GF_DOMEventTarget *gf_dom_event_get_target_from_node(GF_Node *n)
{
GF_DOMEventTarget *target = NULL;
if (!n->sgprivate->interact) {
GF_SAFEALLOC(n->sgprivate->interact, struct _node_interactive_ext);
if (!n->sgprivate->interact) return NULL;
}
if (!n->sgprivate->interact->dom_evt) {
n->sgprivate->interact->dom_evt = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_NODE, n);
}
target = n->sgprivate->interact->dom_evt;
return target;
}
#endif