LCOV - code coverage report
Current view: top level - compositor - events.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 380 1018 37.3 %
Date: 2021-04-29 23:48:07 Functions: 17 22 77.3 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2000-2012
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / Scene Compositor sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : #include "visual_manager.h"
      27             : #include "nodes_stacks.h"
      28             : #include "mpeg4_grouping.h"
      29             : #include <gpac/options.h>
      30             : #include <gpac/utf.h>
      31             : 
      32             : static GF_Node *browse_parent_for_focus(GF_Compositor *compositor, GF_Node *elt, Bool prev_focus);
      33             : 
      34             : static void gf_sc_reset_collide_cursor(GF_Compositor *compositor)
      35             : {
      36             :         if (compositor->sensor_type == GF_CURSOR_COLLIDE) {
      37             :                 GF_Event evt;
      38           1 :                 compositor->sensor_type = evt.cursor.cursor_type = GF_CURSOR_NORMAL;
      39           1 :                 evt.type = GF_EVENT_SET_CURSOR;
      40           1 :                 compositor->video_out->ProcessEvent(compositor->video_out, &evt);
      41             :         }
      42             : }
      43             : 
      44             : 
      45             : static Bool exec_text_selection(GF_Compositor *compositor, GF_Event *event)
      46             : {
      47        3923 :         if (event->type>GF_EVENT_MOUSEMOVE) return GF_FALSE;
      48             : 
      49        3923 :         if (compositor->edited_text)
      50             :                 return GF_FALSE;
      51        3923 :         if (compositor->text_selection )
      52          22 :                 return compositor->hit_text ? GF_TRUE : GF_FALSE;
      53        3901 :         switch (event->type) {
      54             :         case GF_EVENT_MOUSEMOVE:
      55             :                 if (compositor->text_selection && compositor->hit_text)
      56             :                         return GF_TRUE;
      57             :                 break;
      58         163 :         case GF_EVENT_MOUSEDOWN:
      59         163 :                 if (compositor->hit_text) {
      60           9 :                         compositor->text_selection = compositor->hit_text;
      61             :                         /*return 0: the event may be consumed by the tree*/
      62             :                         return GF_FALSE;
      63             :                 }
      64             :                 break;
      65             :         }
      66             :         return GF_FALSE;
      67             : }
      68             : 
      69           8 : static void flush_text_node_edit(GF_Compositor *compositor, Bool final_flush)
      70             : {
      71             :         Bool signal;
      72           8 :         if (!compositor->edited_text) return;
      73             : 
      74             :         /* if this is the final editing and there is text,
      75             :         we need to remove the caret from the text selection buffer */
      76           0 :         if (final_flush && compositor->sel_buffer_len) {
      77           0 :                 memmove(&compositor->sel_buffer[compositor->caret_pos], &compositor->sel_buffer[compositor->caret_pos+1], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos));
      78           0 :                 compositor->sel_buffer_len--;
      79           0 :                 compositor->sel_buffer[compositor->sel_buffer_len] = 0;
      80             :         }
      81             : 
      82             :         /* Recomputes the edited text */
      83           0 :         if (*compositor->edited_text) {
      84           0 :                 gf_free(*compositor->edited_text);
      85           0 :                 *compositor->edited_text = NULL;
      86             :         }
      87           0 :         if (compositor->sel_buffer_len) {
      88             :                 char *txt;
      89             :                 size_t len;
      90             :                 const u16 *lptr;
      91           0 :                 txt = (char*)gf_malloc(sizeof(char)*2*compositor->sel_buffer_len);
      92           0 :                 lptr = compositor->sel_buffer;
      93           0 :                 len = gf_utf8_wcstombs(txt, 2*compositor->sel_buffer_len, &lptr);
      94           0 :                 txt[len] = 0;
      95           0 :                 *compositor->edited_text = gf_strdup(txt);
      96           0 :                 gf_free(txt);
      97             :         }
      98             : 
      99             :         signal = final_flush;
     100           0 :         if ((compositor->focus_text_type==4) && (final_flush==1)) signal = GF_FALSE;
     101             : 
     102           0 :         gf_node_dirty_set(compositor->focus_node, 0, GF_TRUE);
     103             :         //(compositor->focus_text_type==2));
     104           0 :         gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
     105             :         /*notify compositor that text has been edited, in order to update composite textures*/
     106             :         //compositor->text_edit_changed = 1;
     107             : 
     108           0 :         gf_node_set_private(compositor->focus_highlight->node, NULL);
     109             : 
     110             :         /* if this is the final flush, we free the selection buffer and edited text buffer
     111             :         and signal a text content change in the focus node */
     112           0 :         if (final_flush) {
     113             :                 GF_FieldInfo info;
     114           0 :                 if (compositor->sel_buffer) gf_free(compositor->sel_buffer);
     115           0 :                 compositor->sel_buffer = NULL;
     116           0 :                 compositor->sel_buffer_len = compositor->sel_buffer_alloc = 0;
     117           0 :                 compositor->edited_text = NULL;
     118             : 
     119           0 :                 if (compositor->focus_node && signal) {
     120             :                         memset(&info, 0, sizeof(GF_FieldInfo));
     121           0 :                         info.fieldIndex = (u32) -1;
     122           0 :                         if (compositor->focus_text_type>=3) {
     123           0 :                                 gf_node_get_field(compositor->focus_node, 0, &info);
     124             : #ifndef GPAC_DISABLE_VRML
     125           0 :                                 gf_node_event_out(compositor->focus_node, 0);
     126             : #endif
     127             :                         }
     128           0 :                         gf_node_changed(compositor->focus_node, &info);
     129             :                 }
     130             :         }
     131             : }
     132             : 
     133             : 
     134           2 : GF_Err gf_sc_paste_text(GF_Compositor *compositor, const char *text)
     135             : {
     136             :         u16 *conv_buf;
     137             :         size_t len;
     138           2 :         if (!compositor->sel_buffer || !compositor->edited_text) return GF_BAD_PARAM;
     139           0 :         if (!text) return GF_OK;
     140           0 :         len = strlen(text);
     141           0 :         if (!len) return GF_OK;
     142             : 
     143           0 :         gf_sc_lock(compositor, GF_TRUE);
     144             : 
     145           0 :         conv_buf = (u16*)gf_malloc(sizeof(u16)*(len+1));
     146           0 :         len = gf_utf8_mbstowcs(conv_buf, len, &text);
     147             : 
     148           0 :         compositor->sel_buffer_alloc += (u32) len;
     149           0 :         if (compositor->sel_buffer_len == compositor->sel_buffer_alloc)
     150           0 :                 compositor->sel_buffer_alloc++;
     151             : 
     152           0 :         compositor->sel_buffer = (u16*)gf_realloc(compositor->sel_buffer, sizeof(u16)*compositor->sel_buffer_alloc);
     153           0 :         memmove(&compositor->sel_buffer[compositor->caret_pos+len], &compositor->sel_buffer[compositor->caret_pos], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos));
     154           0 :         memcpy(&compositor->sel_buffer[compositor->caret_pos], conv_buf, sizeof(u16)*len);
     155           0 :         gf_free(conv_buf);
     156           0 :         compositor->sel_buffer_len += (u32) len;
     157           0 :         compositor->caret_pos += (u32) len;
     158           0 :         compositor->sel_buffer[compositor->sel_buffer_len]=0;
     159           0 :         flush_text_node_edit(compositor, GF_FALSE);
     160           0 :         gf_sc_lock(compositor, GF_FALSE);
     161             : 
     162           0 :         return GF_OK;
     163             : }
     164           0 : static Bool load_text_node(GF_Compositor *compositor, u32 cmd_type)
     165             : {
     166             :         char **res = NULL;
     167             :         u32 prev_pos, pos=0;
     168             :         s32 caret_pos;
     169             :         Bool append;
     170             : #ifndef GPAC_DISABLE_SVG
     171             :         Bool delete_cr = GF_FALSE;
     172             : #endif
     173             : 
     174             :         caret_pos = -1;
     175             : 
     176             :         append = GF_FALSE;
     177           0 :         switch (cmd_type) {
     178             :         /*load last text chunk*/
     179             :         case 0:
     180             :                 pos = 0;
     181             :                 break;
     182             :         /*load prev text chunk*/
     183           0 :         case 4:
     184             : #ifndef GPAC_DISABLE_SVG
     185             :                 delete_cr = GF_TRUE;
     186             : #endif
     187           0 :         case 1:
     188           0 :                 if (compositor->dom_text_pos == 0) return GF_FALSE;
     189           0 :                 pos = compositor->dom_text_pos - 1;
     190           0 :                 break;
     191             :         /*load next text chunk*/
     192           0 :         case 2:
     193           0 :                 pos = compositor->dom_text_pos + 1;
     194             :                 caret_pos = 0;
     195           0 :                 break;
     196             :         /*split text chunk/append new*/
     197           0 :         case 3:
     198             :                 append = GF_TRUE;
     199           0 :                 pos = compositor->dom_text_pos;
     200             :                 caret_pos = 0;
     201           0 :                 break;
     202             :         }
     203           0 :         prev_pos = compositor->dom_text_pos;
     204           0 :         compositor->dom_text_pos = 0;
     205             : 
     206             : 
     207             : #ifndef GPAC_DISABLE_VRML
     208           0 :         if (compositor->focus_text_type>=3) {
     209           0 :                 MFString *mf = &((M_Text*)compositor->focus_node)->string;
     210             : 
     211           0 :                 if (append) {
     212           0 :                         gf_sg_vrml_mf_append(mf, GF_SG_VRML_MFSTRING, (void **)res);
     213           0 :                         compositor->dom_text_pos = mf->count;
     214           0 :                 } else if (!cmd_type) {
     215           0 :                         compositor->dom_text_pos = mf->count;
     216           0 :                 } else if (pos <= mf->count) {
     217           0 :                         compositor->dom_text_pos = pos;
     218             :                 } else {
     219           0 :                         compositor->dom_text_pos = prev_pos;
     220           0 :                         return GF_FALSE;
     221             :                 }
     222             : 
     223           0 :                 if (compositor->picked_span_idx >=0) {
     224           0 :                         compositor->dom_text_pos = 1+compositor->picked_span_idx;
     225           0 :                         compositor->picked_span_idx = -1;
     226             :                 }
     227             :                 /*take care of loading empty text nodes*/
     228           0 :                 if (!mf->count) {
     229           0 :                         mf->count = 1;
     230           0 :                         mf->vals = (char**)gf_malloc(sizeof(char*));
     231           0 :                         mf->vals[0] = gf_strdup("");
     232             :                 }
     233           0 :                 if (!mf->vals[0]) mf->vals[0] = gf_strdup("");
     234             : 
     235           0 :                 if (!compositor->dom_text_pos || (compositor->dom_text_pos>mf->count)) {
     236           0 :                         compositor->dom_text_pos = prev_pos;
     237           0 :                         return GF_FALSE;
     238             :                 }
     239           0 :                 res = &mf->vals[compositor->dom_text_pos-1];
     240           0 :                 if (compositor->picked_glyph_idx>=0) {
     241             :                         caret_pos = compositor->picked_glyph_idx;
     242           0 :                         compositor->picked_glyph_idx = -1;
     243             : 
     244           0 :                         if (caret_pos > (s32) strlen(*res))
     245             :                                 caret_pos = -1;
     246             :                 }
     247             : 
     248             :         } else
     249             : #endif /*GPAC_DISABLE_VRML*/
     250             : 
     251           0 :                 if (compositor->focus_node) {
     252             : #ifndef GPAC_DISABLE_SVG
     253           0 :                         GF_ChildNodeItem *child = ((GF_ParentNode *) compositor->focus_node)->children;
     254             : 
     255           0 :                         while (child) {
     256           0 :                                 switch (gf_node_get_tag(child->node)) {
     257             :                                 case TAG_DOMText:
     258             :                                         break;
     259           0 :                                 default:
     260           0 :                                         child = child->next;
     261           0 :                                         continue;
     262             :                                 }
     263           0 :                                 compositor->dom_text_pos++;
     264           0 :                                 if (!cmd_type) res = &((GF_DOMText *)child->node)->textContent;
     265           0 :                                 else if (pos==compositor->dom_text_pos) {
     266           0 :                                         if (append) {
     267             :                                                 u16 end;
     268             :                                                 const u16 *srcp;
     269             :                                                 GF_DOMText *ntext;
     270           0 :                                                 GF_ChildNodeItem *children = ((GF_ParentNode *) compositor->focus_node)->children;
     271           0 :                                                 GF_Node *t = gf_node_new(gf_node_get_graph(child->node), TAG_SVG_tbreak);
     272             : 
     273           0 :                                                 gf_node_init(t);
     274           0 :                                                 gf_node_register(t, compositor->focus_node);
     275             : 
     276           0 :                                                 pos = gf_node_list_find_child(children, child->node);
     277             : 
     278             :                                                 /*we're only inserting a tbreak*/
     279           0 :                                                 if (!compositor->caret_pos) {
     280           0 :                                                         gf_node_list_insert_child(&children, t, pos);
     281           0 :                                                         res = &((GF_DOMText *)child->node)->textContent;
     282             :                                                 } else {
     283             :                                                         size_t len;
     284             :                                                         GF_DOMText *cur;
     285             : 
     286           0 :                                                         gf_node_list_insert_child(&children, t, pos+1);
     287           0 :                                                         ntext = (GF_DOMText*) gf_node_new(gf_node_get_graph(child->node), TAG_DOMText);
     288           0 :                                                         gf_node_init(t);
     289           0 :                                                         gf_node_list_insert_child(&children, (GF_Node *)ntext, pos+2);
     290           0 :                                                         gf_node_register((GF_Node*)ntext, compositor->focus_node);
     291             : 
     292           0 :                                                         cur = (GF_DOMText*) child->node;
     293             : 
     294           0 :                                                         gf_free(cur->textContent);
     295           0 :                                                         end = compositor->sel_buffer[compositor->caret_pos];
     296           0 :                                                         compositor->sel_buffer[compositor->caret_pos] = 0;
     297           0 :                                                         len = gf_utf8_wcslen(compositor->sel_buffer);
     298           0 :                                                         cur->textContent = (char*)gf_malloc(sizeof(char)*(len+1));
     299           0 :                                                         srcp = compositor->sel_buffer;
     300           0 :                                                         len = gf_utf8_wcstombs(cur->textContent, len, &srcp);
     301           0 :                                                         cur->textContent[len] = 0;
     302           0 :                                                         compositor->sel_buffer[compositor->caret_pos] = end;
     303             : 
     304           0 :                                                         if (compositor->caret_pos+1<compositor->sel_buffer_len) {
     305           0 :                                                                 len = gf_utf8_wcslen(compositor->sel_buffer + compositor->caret_pos + 1);
     306           0 :                                                                 ntext->textContent = (char*)gf_malloc(sizeof(char)*(len+1));
     307           0 :                                                                 srcp = compositor->sel_buffer + compositor->caret_pos + 1;
     308           0 :                                                                 len = gf_utf8_wcstombs(ntext->textContent, len, &srcp);
     309           0 :                                                                 ntext->textContent[len] = 0;
     310             :                                                         } else {
     311           0 :                                                                 ntext->textContent = gf_strdup("");
     312             :                                                         }
     313           0 :                                                         res = &ntext->textContent;
     314           0 :                                                         compositor->dom_text_pos ++;
     315           0 :                                                         compositor->edited_text = NULL;
     316             :                                                 }
     317             : 
     318             :                                         } else {
     319           0 :                                                 if (delete_cr && child->next) {
     320           0 :                                                         GF_Node *tbreak = child->next->node;
     321           0 :                                                         GF_ChildNodeItem *children = ((GF_ParentNode *) compositor->focus_node)->children;
     322           0 :                                                         gf_node_list_del_child(&children, tbreak);
     323           0 :                                                         gf_node_unregister(tbreak, compositor->focus_node);
     324           0 :                                                         if (child->next && (gf_node_get_tag(child->next->node)==TAG_DOMText) ) {
     325           0 :                                                                 GF_DOMText *n1 = (GF_DOMText *)child->node;
     326           0 :                                                                 GF_DOMText *n2 = (GF_DOMText *)child->next->node;
     327             : 
     328           0 :                                                                 if (compositor->edited_text) {
     329           0 :                                                                         flush_text_node_edit(compositor, GF_TRUE);
     330             :                                                                 }
     331           0 :                                                                 if (!n1->textContent) n1->textContent = gf_strdup("");
     332           0 :                                                                 caret_pos = (u32) strlen(n1->textContent);
     333           0 :                                                                 if (n2->textContent) {
     334           0 :                                                                         n1->textContent = (char*)gf_realloc(n1->textContent, sizeof(char)*(strlen(n1->textContent)+strlen(n2->textContent)+1));
     335           0 :                                                                         strcat(n1->textContent, n2->textContent);
     336             :                                                                 }
     337           0 :                                                                 gf_node_list_del_child(&children, (GF_Node*)n2);
     338           0 :                                                                 gf_node_unregister((GF_Node*)n2, compositor->focus_node);
     339           0 :                                                                 compositor->edited_text = NULL;
     340             :                                                         }
     341             :                                                 }
     342           0 :                                                 res = &((GF_DOMText *)child->node)->textContent;
     343             :                                         }
     344             :                                         break;
     345             :                                 }
     346           0 :                                 child = child->next;
     347           0 :                                 continue;
     348             :                         }
     349             :                         /*load of an empty text*/
     350           0 :                         if (!res && !cmd_type) {
     351           0 :                                 GF_DOMText *t = gf_dom_add_text_node(compositor->focus_node, gf_strdup(""));
     352           0 :                                 res = &t->textContent;
     353             :                         }
     354             : 
     355           0 :                         if (!res) {
     356           0 :                                 compositor->dom_text_pos = prev_pos;
     357           0 :                                 return GF_FALSE;
     358             :                         }
     359             : #endif
     360             :                 }
     361             : 
     362           0 :         if (compositor->edited_text) {
     363           0 :                 flush_text_node_edit(compositor, GF_TRUE);
     364             :         }
     365             : 
     366           0 :         if (res && *res && strlen(*res) ) {
     367           0 :                 const char *src = *res;
     368           0 :                 compositor->sel_buffer_alloc = 2 + (u32) strlen(src);
     369           0 :                 compositor->sel_buffer = (u16*)gf_realloc(compositor->sel_buffer, sizeof(u16)*compositor->sel_buffer_alloc);
     370             : 
     371           0 :                 if (caret_pos>=0) {
     372           0 :                         size_t l = gf_utf8_mbstowcs(compositor->sel_buffer, compositor->sel_buffer_alloc, &src);
     373           0 :                         compositor->sel_buffer_len = (u32) l;
     374           0 :                         memmove(&compositor->sel_buffer[caret_pos+1], &compositor->sel_buffer[caret_pos], sizeof(u16)*(compositor->sel_buffer_len-caret_pos));
     375           0 :                         compositor->sel_buffer[caret_pos] = GF_CARET_CHAR;
     376           0 :                         compositor->caret_pos = caret_pos;
     377             : 
     378             :                 } else {
     379           0 :                         size_t l = gf_utf8_mbstowcs(compositor->sel_buffer, compositor->sel_buffer_alloc, &src);
     380           0 :                         compositor->sel_buffer_len = (u32) l;
     381           0 :                         compositor->sel_buffer[compositor->sel_buffer_len] = GF_CARET_CHAR;
     382           0 :                         compositor->caret_pos = compositor->sel_buffer_len;
     383             :                 }
     384           0 :                 compositor->sel_buffer_len++;
     385           0 :                 compositor->sel_buffer[compositor->sel_buffer_len]=0;
     386             :         } else {
     387           0 :                 compositor->sel_buffer_alloc = 2;
     388           0 :                 compositor->sel_buffer = (u16*)gf_malloc(sizeof(u16)*2);
     389           0 :                 compositor->sel_buffer[0] = GF_CARET_CHAR;
     390           0 :                 compositor->sel_buffer[1] = 0;
     391           0 :                 compositor->caret_pos = 0;
     392           0 :                 compositor->sel_buffer_len = 1;
     393             :         }
     394           0 :         compositor->edited_text = res;
     395           0 :         compositor->text_edit_changed = GF_TRUE;
     396           0 :         flush_text_node_edit(compositor, GF_FALSE);
     397           0 :         return GF_TRUE;
     398             : }
     399             : 
     400           0 : static void exec_text_input(GF_Compositor *compositor, GF_Event *event)
     401             : {
     402             :         Bool is_end = GF_FALSE;
     403             : 
     404           0 :         if (!event) {
     405           0 :                 load_text_node(compositor, 0);
     406           0 :                 return;
     407           0 :         } else if (event->type==GF_EVENT_TEXTINPUT) {
     408           0 :                 u32 unicode_char = event->character.unicode_char;
     409             :                 //filter all non-text symbols
     410           0 :                 if (unicode_char <= 31) {
     411             :                         return;
     412             :                 }
     413             : 
     414             :                 {
     415             : #ifndef GPAC_DISABLE_SVG
     416             :                         GF_DOM_Event evt;
     417             :                         GF_Node *target;
     418             :                         /*send text input event*/
     419             :                         memset(&evt, 0, sizeof(GF_DOM_Event));
     420           0 :                         evt.key_flags = event->key.flags;
     421           0 :                         evt.bubbles = 1;
     422           0 :                         evt.cancelable = 1;
     423           0 :                         evt.type = event->type;
     424           0 :                         evt.detail = unicode_char;
     425           0 :                         target = compositor->focus_node;
     426           0 :                         if (!target) target = gf_sg_get_root_node(compositor->scene);
     427           0 :                         gf_dom_event_fire(target, &evt);
     428             :                         /*event has been cancelled*/
     429           0 :                         if (evt.event_phase & GF_DOM_EVENT_CANCEL_MASK) return;
     430             : 
     431           0 :                         if (compositor->sel_buffer_len + 1 == compositor->sel_buffer_alloc) {
     432           0 :                                 compositor->sel_buffer_alloc += 10;
     433           0 :                                 compositor->sel_buffer = (u16*)gf_realloc(compositor->sel_buffer, sizeof(u16)*compositor->sel_buffer_alloc);
     434             :                         }
     435           0 :                         memmove(&compositor->sel_buffer[compositor->caret_pos+1], &compositor->sel_buffer[compositor->caret_pos], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos));
     436           0 :                         compositor->sel_buffer[compositor->caret_pos] = unicode_char;
     437           0 :                         compositor->sel_buffer_len++;
     438           0 :                         compositor->caret_pos++;
     439           0 :                         compositor->sel_buffer[compositor->sel_buffer_len] = 0;
     440             : #endif
     441             :                 }
     442           0 :         } else if (event->type==GF_EVENT_KEYDOWN) {
     443           0 :                 u32 prev_caret = compositor->caret_pos;
     444           0 :                 switch (event->key.key_code) {
     445             :                 /*end of edit mode*/
     446             :                 case GF_KEY_ESCAPE:
     447             :                         is_end = GF_TRUE;
     448             :                         break;
     449           0 :                 case GF_KEY_LEFT:
     450           0 :                         if (compositor->caret_pos) {
     451           0 :                                 if (event->key.flags & GF_KEY_MOD_CTRL) {
     452           0 :                                         while (compositor->caret_pos && (compositor->sel_buffer[compositor->caret_pos] != ' '))
     453           0 :                                                 compositor->caret_pos--;
     454             :                                 } else {
     455           0 :                                         compositor->caret_pos--;
     456             :                                 }
     457             :                         }
     458             :                         else {
     459           0 :                                 load_text_node(compositor, 1);
     460           0 :                                 return;
     461             :                         }
     462             :                         break;
     463           0 :                 case GF_KEY_RIGHT:
     464           0 :                         if (compositor->caret_pos+1<compositor->sel_buffer_len) {
     465           0 :                                 if (event->key.flags & GF_KEY_MOD_CTRL) {
     466           0 :                                         while ((compositor->caret_pos+1<compositor->sel_buffer_len)
     467           0 :                                                 && (compositor->sel_buffer[compositor->caret_pos] != ' '))
     468           0 :                                                 compositor->caret_pos++;
     469             :                                 } else {
     470           0 :                                         compositor->caret_pos++;
     471             :                                 }
     472             :                         }
     473             :                         else {
     474           0 :                                 load_text_node(compositor, 2);
     475           0 :                                 return;
     476             :                         }
     477             :                         break;
     478           0 :                 case GF_KEY_HOME:
     479           0 :                         compositor->caret_pos = 0;
     480             :                         break;
     481           0 :                 case GF_KEY_END:
     482           0 :                         compositor->caret_pos = compositor->sel_buffer_len-1;
     483             :                         break;
     484           0 :                 case GF_KEY_TAB:
     485           0 :                         if (!load_text_node(compositor, (event->key.flags & GF_KEY_MOD_SHIFT) ? 1 : 2)) {
     486             :                                 is_end = GF_TRUE;
     487             :                                 break;
     488             :                         }
     489             :                         return;
     490           0 :                 case GF_KEY_BACKSPACE:
     491           0 :                         if (compositor->caret_pos) {
     492           0 :                                 if (compositor->sel_buffer_len) compositor->sel_buffer_len--;
     493           0 :                                 memmove(&compositor->sel_buffer[compositor->caret_pos-1], &compositor->sel_buffer[compositor->caret_pos], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos+1));
     494           0 :                                 compositor->sel_buffer[compositor->sel_buffer_len]=0;
     495           0 :                                 compositor->caret_pos--;
     496           0 :                                 flush_text_node_edit(compositor, GF_FALSE);
     497             :                         } else {
     498           0 :                                 load_text_node(compositor, 4);
     499             :                         }
     500             :                         return;
     501           0 :                 case GF_KEY_DEL:
     502           0 :                         if (compositor->caret_pos+1<compositor->sel_buffer_len) {
     503           0 :                                 if (compositor->sel_buffer_len) compositor->sel_buffer_len--;
     504           0 :                                 memmove(&compositor->sel_buffer[compositor->caret_pos+1], &compositor->sel_buffer[compositor->caret_pos+2], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos-1));
     505           0 :                                 compositor->sel_buffer[compositor->sel_buffer_len]=0;
     506           0 :                                 flush_text_node_edit(compositor, GF_FALSE);
     507           0 :                                 return;
     508             :                         }
     509             :                         break;
     510           0 :                 case GF_KEY_ENTER:
     511           0 :                         if (compositor->focus_text_type==4) {
     512           0 :                                 flush_text_node_edit(compositor, 2);
     513             :                                 break;
     514             :                         }
     515           0 :                         load_text_node(compositor, 3);
     516           0 :                         return;
     517             :                 default:
     518             :                         return;
     519             :                 }
     520           0 :                 if (!is_end && compositor->sel_buffer) {
     521           0 :                         if (compositor->caret_pos==prev_caret) return;
     522           0 :                         memmove(&compositor->sel_buffer[prev_caret], &compositor->sel_buffer[prev_caret+1], sizeof(u16)*(compositor->sel_buffer_len-prev_caret));
     523           0 :                         memmove(&compositor->sel_buffer[compositor->caret_pos+1], &compositor->sel_buffer[compositor->caret_pos], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos));
     524           0 :                         compositor->sel_buffer[compositor->caret_pos]=GF_CARET_CHAR;
     525             :                 }
     526             :         } else {
     527             :                 return;
     528             :         }
     529             : 
     530           0 :         flush_text_node_edit(compositor, is_end);
     531             : }
     532             : 
     533          32 : static void toggle_keyboard(GF_Compositor * compositor, Bool do_show)
     534             : {
     535             :         GF_Event evt;
     536             :         memset(&evt, 0, sizeof(GF_Event));
     537          32 :         evt.type = do_show ? GF_EVENT_TEXT_EDITING_START : GF_EVENT_TEXT_EDITING_END;
     538             : 
     539          32 :         if (compositor->video_out) {
     540          32 :                 GF_Err e = compositor->video_out->ProcessEvent(compositor->video_out, &evt);
     541          64 :                 if (e == GF_OK) return;
     542             :         }
     543           0 :         gf_sc_user_event(compositor, &evt);
     544             : }
     545             : 
     546         204 : static Bool hit_node_editable(GF_Compositor *compositor, Bool check_focus_node)
     547             : {
     548             : #ifndef GPAC_DISABLE_SVG
     549             :         SVGAllAttributes atts;
     550             : #endif
     551             :         u32 tag;
     552         204 :         GF_Node *text = check_focus_node ? compositor->focus_node : compositor->hit_node;
     553         204 :         if (!text) {
     554          22 :                 toggle_keyboard(compositor, GF_FALSE);
     555          22 :                 return GF_FALSE;
     556             :         }
     557         182 :         if (compositor->hit_node==compositor->focus_node) return compositor->focus_text_type ? GF_TRUE : GF_FALSE;
     558             : 
     559         182 :         tag = gf_node_get_tag(text);
     560             : 
     561             : #ifndef GPAC_DISABLE_VRML
     562         182 :         switch (tag) {
     563          10 :         case TAG_MPEG4_Text:
     564             : #ifndef GPAC_DISABLE_X3D
     565             :         case TAG_X3D_Text:
     566             : #endif
     567             :         {
     568          10 :                 M_FontStyle *fs = (M_FontStyle *) ((M_Text *)text)->fontStyle;
     569          10 :                 if (!fs || !fs->style.buffer) return GF_FALSE;
     570          10 :                 if (strstr(fs->style.buffer, "editable") || strstr(fs->style.buffer, "EDITABLE")) {
     571           0 :                         compositor->focus_text_type = 3;
     572          10 :                 } else if (strstr(fs->style.buffer, "simple_edit") || strstr(fs->style.buffer, "SIMPLE_EDIT")) {
     573           0 :                         compositor->focus_text_type = 4;
     574             :                 } else {
     575          10 :                         toggle_keyboard(compositor, GF_FALSE);
     576          10 :                         return GF_FALSE;
     577             :                 }
     578           0 :                 compositor->focus_node = text;
     579           0 :                 toggle_keyboard(compositor, compositor->focus_text_type > 2 ? GF_TRUE : GF_FALSE);
     580           0 :                 return GF_TRUE;
     581             :         }
     582             :         default:
     583             :                 break;
     584             :         }
     585             : #endif /*GPAC_DISABLE_VRML*/
     586         172 :         if (tag <= GF_NODE_FIRST_DOM_NODE_TAG) return GF_FALSE;
     587             : #ifndef GPAC_DISABLE_SVG
     588          28 :         gf_svg_flatten_attributes((SVG_Element *)text, &atts);
     589          28 :         if (!atts.editable || !*atts.editable) return GF_FALSE;
     590           0 :         switch (tag) {
     591           0 :         case TAG_SVG_text:
     592             :         case TAG_SVG_textArea:
     593           0 :                 compositor->focus_text_type = 1;
     594           0 :                 break;
     595           0 :         case TAG_SVG_tspan:
     596           0 :                 compositor->focus_text_type = 2;
     597           0 :                 break;
     598             :         default:
     599             :                 return GF_FALSE;
     600             :         }
     601           0 :         if (compositor->focus_node != text) {
     602             :                 GF_DOM_Event evt;
     603             :                 memset(&evt, 0, sizeof(GF_DOM_Event));
     604           0 :                 evt.bubbles = 1;
     605           0 :                 evt.type = GF_EVENT_FOCUSOUT;
     606           0 :                 gf_dom_event_fire(compositor->focus_node, &evt);
     607             : 
     608           0 :                 compositor->focus_node = text;
     609           0 :                 evt.type = GF_EVENT_FOCUSIN;
     610           0 :                 gf_dom_event_fire(compositor->focus_node, &evt);
     611           0 :                 compositor->focus_uses_dom_events = GF_TRUE;
     612             :         }
     613           0 :         compositor->hit_node = NULL;
     614           0 :         toggle_keyboard(compositor, compositor->focus_text_type > 0 ? GF_TRUE : GF_FALSE);
     615             : #endif
     616           0 :         return GF_TRUE;
     617             : }
     618             : 
     619             : #ifndef GPAC_DISABLE_SVG
     620           0 : static GF_Node *get_parent_focus(GF_Node *node, GF_List *hit_use_stack, u32 cur_idx)
     621             : {
     622             :         GF_Node *parent;
     623             :         GF_FieldInfo info;
     624           0 :         if (!node) return NULL;
     625             : 
     626           0 :         if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_focusable, GF_FALSE, GF_FALSE, &info)==GF_OK) {
     627           0 :                 if ( *(SVG_Focusable*)info.far_ptr == SVG_FOCUSABLE_TRUE) return node;
     628             :         }
     629           0 :         parent = gf_node_get_parent(node, 0);
     630           0 :         if (cur_idx) {
     631           0 :                 GF_Node *n = (GF_Node*)gf_list_get(hit_use_stack, cur_idx-1);
     632           0 :                 if (n==node) {
     633           0 :                         parent = (GF_Node*)gf_list_get(hit_use_stack, cur_idx-2);
     634           0 :                         if (cur_idx>1) cur_idx-=2;
     635             :                         else cur_idx=0;
     636             :                 }
     637             :         }
     638           0 :         return get_parent_focus(parent, hit_use_stack, cur_idx);
     639             : }
     640             : #endif
     641             : 
     642             : 
     643           7 : static Bool exec_event_dom(GF_Compositor *compositor, GF_Event *event)
     644             : {
     645             : #ifndef GPAC_DISABLE_SVG
     646             :         GF_DOM_Event evt;
     647             :         u32 cursor_type;
     648             :         Bool ret = GF_FALSE;
     649             : 
     650             :         cursor_type = GF_CURSOR_NORMAL;
     651             :         /*all mouse events*/
     652           7 :         if (event->type<=GF_EVENT_MOUSEWHEEL) {
     653           7 :                 Fixed X = compositor->hit_world_point.x;
     654           7 :                 Fixed Y = compositor->hit_world_point.y;
     655             :                 /*flip back to origin at top-left*/
     656           7 :                 if (compositor->visual->center_coords) {
     657           0 :                         X += INT2FIX(compositor->visual->width)/2;
     658           0 :                         Y = INT2FIX(compositor->visual->height)/2 - Y;
     659             :                 }
     660             : 
     661           7 :                 if (compositor->hit_node) {
     662             :                         GF_Node *focus;
     663             :                         Bool hit_changed = GF_FALSE;
     664           7 :                         GF_Node *current_use = (GF_Node*)gf_list_last(compositor->hit_use_stack);
     665             :                         memset(&evt, 0, sizeof(GF_DOM_Event));
     666           7 :                         evt.clientX = evt.screenX = FIX2INT(X);
     667           7 :                         evt.clientY = evt.screenY = FIX2INT(Y);
     668           7 :                         evt.bubbles = 1;
     669           7 :                         evt.cancelable = 1;
     670           7 :                         evt.key_flags = compositor->key_states;
     671             : 
     672             :                         /*first check if the hit node is not the grab (previous) node - this may happen regardless of the
     673             :                         mouse actions (animations, ...)*/
     674           7 :                         if ((compositor->grab_node != compositor->hit_node) || (compositor->grab_use != current_use) ) {
     675             :                                 /*mouse out*/
     676           7 :                                 if (compositor->grab_node) {
     677           0 :                                         evt.relatedTarget = compositor->hit_node;
     678           0 :                                         evt.type = GF_EVENT_MOUSEOUT;
     679           0 :                                         ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->prev_hit_use_stack);
     680             :                                         /*prepare mouseOver*/
     681           0 :                                         evt.relatedTarget = compositor->grab_node;
     682             :                                 }
     683           7 :                                 compositor->grab_node = compositor->hit_node;
     684           7 :                                 compositor->grab_use = current_use;
     685             : 
     686             :                                 /*mouse over*/
     687           7 :                                 evt.type = GF_EVENT_MOUSEOVER;
     688           7 :                                 ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack);
     689             :                                 hit_changed = GF_TRUE;
     690             :                         }
     691           7 :                         switch (event->type) {
     692           7 :                         case GF_EVENT_MOUSEMOVE:
     693           7 :                                 evt.cancelable = 0;
     694           7 :                                 if (!hit_changed) {
     695           0 :                                         evt.type = GF_EVENT_MOUSEMOVE;
     696           0 :                                         ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack);
     697             :                                 }
     698           7 :                                 compositor->num_clicks = 0;
     699           7 :                                 break;
     700           0 :                         case GF_EVENT_MOUSEDOWN:
     701           0 :                                 if ((compositor->grab_x != X) || (compositor->grab_y != Y)) compositor->num_clicks = 0;
     702           0 :                                 evt.type = GF_EVENT_MOUSEDOWN;
     703           0 :                                 compositor->num_clicks ++;
     704           0 :                                 evt.button = event->mouse.button;
     705           0 :                                 evt.detail = compositor->num_clicks;
     706             : 
     707           0 :                                 ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack);
     708           0 :                                 compositor->grab_x = X;
     709           0 :                                 compositor->grab_y = Y;
     710             : 
     711             :                                 /*change focus*/
     712           0 :                                 focus = get_parent_focus(compositor->grab_node, compositor->hit_use_stack, gf_list_count(compositor->hit_use_stack));
     713           0 :                                 if (focus) gf_sc_focus_switch_ring(compositor, GF_FALSE, focus, 1);
     714           0 :                                 else if (compositor->focus_node) gf_sc_focus_switch_ring(compositor, GF_FALSE, NULL, 1);
     715             : 
     716             :                                 break;
     717           0 :                         case GF_EVENT_MOUSEUP:
     718           0 :                                 evt.type = GF_EVENT_MOUSEUP;
     719           0 :                                 evt.button = event->mouse.button;
     720           0 :                                 evt.detail = compositor->num_clicks;
     721           0 :                                 ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack);
     722             :                                 /*
     723             :                                 TODO quick- fix for iPhone as well
     724             :                                 TODO clean: figure out whether we use a mouse or a touch device - if touch device, remove this test
     725             :                                 */
     726             : #if !defined(_WIN32_WCE) || !defined(GPAC_CONFIG_ANDROID)
     727           0 :                                 if ((compositor->grab_x == X) && (compositor->grab_y == Y))
     728             : #endif
     729             :                                 {
     730           0 :                                         evt.type = GF_EVENT_CLICK;
     731           0 :                                         ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack);
     732             :                                 }
     733             :                                 break;
     734           0 :                         case GF_EVENT_MOUSEWHEEL:
     735           0 :                                 evt.type = GF_EVENT_MOUSEWHEEL;
     736           0 :                                 evt.button = event->mouse.button;
     737           0 :                                 evt.new_scale = event->mouse.wheel_pos;
     738           0 :                                 ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack);
     739           0 :                                 break;
     740             :                         }
     741           7 :                         cursor_type = evt.has_ui_events ? GF_CURSOR_TOUCH : GF_CURSOR_NORMAL;
     742             :                 } else {
     743             :                         /*mouse out*/
     744           0 :                         if (compositor->grab_node) {
     745             :                                 memset(&evt, 0, sizeof(GF_DOM_Event));
     746           0 :                                 evt.clientX = evt.screenX = FIX2INT(X);
     747           0 :                                 evt.clientY = evt.screenY = FIX2INT(Y);
     748           0 :                                 evt.bubbles = 1;
     749           0 :                                 evt.cancelable = 1;
     750           0 :                                 evt.key_flags = compositor->key_states;
     751           0 :                                 evt.type = GF_EVENT_MOUSEOUT;
     752           0 :                                 ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->prev_hit_use_stack);
     753             :                         }
     754             : 
     755             :                         /*reset focus*/
     756           0 :                         if (compositor->focus_node && (event->type==GF_EVENT_MOUSEDOWN))
     757           0 :                                 gf_sc_focus_switch_ring(compositor, GF_FALSE, NULL, 1);
     758             : 
     759           0 :                         compositor->grab_node = NULL;
     760           0 :                         compositor->grab_use = NULL;
     761             : 
     762             :                         /*dispatch event to root SVG*/
     763             :                         memset(&evt, 0, sizeof(GF_DOM_Event));
     764           0 :                         evt.clientX = evt.screenX = FIX2INT(X);
     765           0 :                         evt.clientY = evt.screenY = FIX2INT(Y);
     766           0 :                         evt.bubbles = 1;
     767           0 :                         evt.cancelable = 1;
     768           0 :                         evt.key_flags = compositor->key_states;
     769           0 :                         evt.type = event->type;
     770           0 :                         evt.button = event->mouse.button;
     771           0 :                         evt.new_scale = event->mouse.wheel_pos;
     772           0 :                         ret += gf_dom_event_fire_ex(gf_sg_get_root_node(compositor->scene), &evt, compositor->hit_use_stack);
     773             :                 }
     774           7 :                 if (compositor->sensor_type != cursor_type) {
     775             :                         GF_Event c_evt;
     776           0 :                         c_evt.type = GF_EVENT_SET_CURSOR;
     777           0 :                         c_evt.cursor.cursor_type = cursor_type;
     778           0 :                         compositor->video_out->ProcessEvent(compositor->video_out, &c_evt);
     779           0 :                         compositor->sensor_type = cursor_type;
     780             :                 }
     781             :         }
     782           0 :         else if ((event->type>=GF_EVENT_KEYUP) && (event->type<=GF_EVENT_LONGKEYPRESS)) {
     783             :                 GF_Node *target;
     784             :                 memset(&evt, 0, sizeof(GF_DOM_Event));
     785           0 :                 evt.key_flags = event->key.flags;
     786           0 :                 evt.bubbles = 1;
     787           0 :                 evt.cancelable = 1;
     788           0 :                 evt.type = event->type;
     789           0 :                 evt.detail = event->key.key_code;
     790           0 :                 evt.key_hw_code = event->key.hw_code;
     791           0 :                 target = compositor->focus_node;
     792             : 
     793             :                 /*dirty hack to simulate browserback*/
     794           0 :                 if (event->key.key_code==GF_KEY_BACKSPACE && (event->key.flags & GF_KEY_MOD_CTRL)) {
     795           0 :                         event->key.key_code = GF_KEY_BROWSERBACK;
     796             :                 }
     797             : 
     798           0 :                 if ((event->key.key_code>=GF_KEY_BROWSERBACK) && (event->key.key_code<=GF_KEY_BROWSERSTOP)) {
     799             :                         target = NULL;
     800             :                 }
     801           0 :                 if (!target) target = gf_sg_get_root_node(compositor->scene);
     802           0 :                 ret += gf_dom_event_fire(target, &evt);
     803             : 
     804           0 :                 if (event->type==GF_EVENT_KEYDOWN) {
     805           0 :                         switch (event->key.key_code) {
     806           0 :                         case GF_KEY_ENTER:
     807           0 :                                 evt.type = GF_EVENT_ACTIVATE;
     808           0 :                                 evt.detail = 0;
     809           0 :                                 ret += gf_dom_event_fire(target, &evt);
     810           0 :                                 break;
     811             :                         }
     812             :                 }
     813           0 :         } else if (event->type == GF_EVENT_TEXTINPUT) {
     814             :                 GF_Node *target;
     815           0 :                 switch (event->character.unicode_char) {
     816             :                 case '\r':
     817             :                 case '\n':
     818             :                 case '\t':
     819             :                         //case '\b':
     820             :                         break;
     821             :                 default:
     822             :                         memset(&evt, 0, sizeof(GF_DOM_Event));
     823           0 :                         evt.key_flags = event->key.flags;
     824           0 :                         evt.bubbles = 1;
     825           0 :                         evt.cancelable = 1;
     826           0 :                         evt.type = event->type;
     827           0 :                         evt.detail = event->character.unicode_char;
     828           0 :                         target = compositor->focus_node;
     829           0 :                         if (!target) target = gf_sg_get_root_node(compositor->scene);
     830           0 :                         ret += gf_dom_event_fire(target, &evt);
     831           0 :                         break;
     832             :                 }
     833           0 :         }
     834           7 :         return ret;
     835             : #else
     836             :         return 0;
     837             : #endif
     838             : }
     839             : 
     840             : #ifndef GPAC_DISABLE_VRML
     841             : 
     842             : 
     843        3897 : Bool gf_sc_exec_event_vrml(GF_Compositor *compositor, GF_Event *ev)
     844             : {
     845             :         u32 res = 0;
     846             :         GF_SensorHandler *hs;
     847             :         GF_List *tmp;
     848             :         u32 i, count, stype;
     849             : 
     850             :         /*reset previous composite texture*/
     851        3897 :         if (compositor->prev_hit_appear != compositor->hit_appear) {
     852           7 :                 if (compositor->prev_hit_appear) {
     853           2 :                         compositor_compositetexture_handle_event(compositor, compositor->prev_hit_appear, ev, GF_TRUE);
     854           2 :                         if (!compositor->grabbed_sensor) compositor->prev_hit_appear = NULL;
     855             :                 }
     856             :         }
     857             : 
     858             :         /*composite texture*/
     859        3897 :         if (compositor->hit_appear) {
     860             :                 GF_Node *appear = compositor->hit_appear;
     861          68 :                 if (compositor_compositetexture_handle_event(compositor, compositor->hit_appear, ev, GF_FALSE)) {
     862          57 :                         if (compositor->hit_appear) compositor->prev_hit_appear = appear;
     863             : 
     864             : 
     865          57 :                         compositor->grabbed_sensor = 0;
     866             :                         /*check if we have grabbed sensors*/
     867          57 :                         count = gf_list_count(compositor->previous_sensors);
     868          57 :                         for (i=0; i<count; i++) {
     869           0 :                                 hs = (GF_SensorHandler*)gf_list_get(compositor->previous_sensors, i);
     870             :                                 /*if sensor is grabbed, add it to the list of active sensor for next pick*/
     871           0 :                                 if (hs->grabbed) {
     872           0 :                                         hs->OnUserEvent(hs, GF_FALSE, GF_TRUE, ev, compositor);
     873           0 :                                         gf_list_add(compositor->sensors, hs);
     874           0 :                                         compositor->grabbed_sensor = 1;
     875             :                                 }
     876             :                         }
     877             : 
     878             :                         return GF_TRUE;
     879             :                 }
     880             : //              compositor->prev_hit_appear = compositor->grabbed_sensor ? compositor->hit_appear : NULL;
     881          11 :                 compositor->prev_hit_appear = compositor->hit_appear;
     882             :         }
     883             : 
     884        3840 :         count = gf_list_count(compositor->sensors);
     885             :         /*if we have a hit node at the compositor level, use "touch" as default cursor - this avoid
     886             :         resetting the cursor when the picked node is a DOM node in a composite texture*/
     887             : //      stype = (compositor->hit_node!=NULL) ? GF_CURSOR_TOUCH : GF_CURSOR_NORMAL;
     888             :         stype = GF_CURSOR_NORMAL;
     889        5929 :         for (i=0; i<count; i++) {
     890             :                 GF_Node *keynav;
     891             :                 Bool check_anchor = GF_FALSE;
     892        2089 :                 hs = (GF_SensorHandler*)gf_list_get(compositor->sensors, i);
     893             : 
     894             :                 /*try to remove this sensor from the previous sensor list*/
     895        2089 :                 gf_list_del_item(compositor->previous_sensors, hs);
     896        2089 :                 if (gf_node_get_id(hs->sensor))
     897        2054 :                         stype = gf_node_get_tag(hs->sensor);
     898             : 
     899        2089 :                 keynav = gf_scene_get_keynav(gf_node_get_graph(hs->sensor), hs->sensor);
     900        2089 :                 if (keynav) gf_sc_change_key_navigator(compositor, keynav);
     901             : 
     902             :                 /*call the sensor LAST, as this may triger a destroy of the scene the sensor is in
     903             :                 this is only true for anchors, as other other sensors output events are queued as routes until next pass*/
     904        2089 :                 res += hs->OnUserEvent(hs, GF_TRUE, GF_FALSE, ev, compositor);
     905        2089 :                 if (stype == TAG_MPEG4_Anchor) check_anchor = GF_TRUE;
     906             : #ifndef GPAC_DISABLE_X3D
     907        2089 :                 else if (stype == TAG_X3D_Anchor) check_anchor = GF_TRUE;
     908             : #endif
     909             :                 if (check_anchor) {
     910             :                         /*subscene with active sensor has been deleted, we cannot continue process the sensors stack*/
     911           0 :                         if (count != gf_list_count(compositor->sensors))
     912             :                                 break;
     913             :                 }
     914             :         }
     915        3840 :         compositor->grabbed_sensor = 0;
     916             :         /*check if we have grabbed sensors*/
     917        3840 :         count = gf_list_count(compositor->previous_sensors);
     918        4329 :         for (i=0; i<count; i++) {
     919         489 :                 hs = (GF_SensorHandler*)gf_list_get(compositor->previous_sensors, i);
     920         489 :                 res += hs->OnUserEvent(hs, GF_FALSE, GF_FALSE, ev, compositor);
     921             :                 /*if sensor is grabbed, add it to the list of active sensor for next pick*/
     922         489 :                 if (hs->grabbed) {
     923         259 :                         gf_list_add(compositor->sensors, hs);
     924         259 :                         compositor->grabbed_sensor = 1;
     925             :                 }
     926         489 :                 stype = gf_node_get_tag(hs->sensor);
     927             :         }
     928        3840 :         gf_list_reset(compositor->previous_sensors);
     929             : 
     930             :         /*switch sensors*/
     931        3840 :         tmp = compositor->sensors;
     932        3840 :         compositor->sensors = compositor->previous_sensors;
     933        3840 :         compositor->previous_sensors = tmp;
     934             : 
     935             :         /*and set cursor*/
     936        3840 :         if (compositor->sensor_type != GF_CURSOR_COLLIDE) {
     937        3839 :                 switch (stype) {
     938             :                 case TAG_MPEG4_Anchor:
     939             :                         stype = GF_CURSOR_ANCHOR;
     940             :                         break;
     941             :                 case TAG_MPEG4_PlaneSensor2D:
     942             :                 case TAG_MPEG4_PlaneSensor:
     943             :                         stype = GF_CURSOR_PLANE;
     944             :                         break;
     945             :                 case TAG_MPEG4_CylinderSensor:
     946             :                 case TAG_MPEG4_DiscSensor:
     947             :                 case TAG_MPEG4_SphereSensor:
     948             :                         stype = GF_CURSOR_ROTATE;
     949             :                         break;
     950             :                 case TAG_MPEG4_ProximitySensor2D:
     951             :                 case TAG_MPEG4_ProximitySensor:
     952             :                         stype = GF_CURSOR_PROXIMITY;
     953             :                         break;
     954             :                 case TAG_MPEG4_TouchSensor:
     955             :                         stype = GF_CURSOR_TOUCH;
     956             :                         break;
     957             : #ifndef GPAC_DISABLE_X3D
     958             :                 case TAG_X3D_Anchor:
     959             :                         stype = GF_CURSOR_ANCHOR;
     960             :                         break;
     961             :                 case TAG_X3D_PlaneSensor:
     962             :                         stype = GF_CURSOR_PLANE;
     963             :                         break;
     964             :                 case TAG_X3D_CylinderSensor:
     965             :                 case TAG_X3D_SphereSensor:
     966             :                         stype = GF_CURSOR_ROTATE;
     967             :                         break;
     968             :                 case TAG_X3D_ProximitySensor:
     969             :                         stype = GF_CURSOR_PROXIMITY;
     970             :                         break;
     971             :                 case TAG_X3D_TouchSensor:
     972             :                         stype = GF_CURSOR_TOUCH;
     973             :                         break;
     974             : #endif
     975             : 
     976             :                 default:
     977             :                         stype = GF_CURSOR_NORMAL;
     978             :                         break;
     979             :                 }
     980        1495 :                 if ((stype != GF_CURSOR_NORMAL) || (compositor->sensor_type != stype)) {
     981             :                         GF_Event evt;
     982        2462 :                         evt.type = GF_EVENT_SET_CURSOR;
     983        2462 :                         evt.cursor.cursor_type = stype;
     984        2462 :                         compositor->video_out->ProcessEvent(compositor->video_out, &evt);
     985        2462 :                         compositor->sensor_type = stype;
     986             :                 }
     987             :         } else {
     988           1 :                 gf_sc_reset_collide_cursor(compositor);
     989             :         }
     990        3840 :         if (res) {
     991             :                 GF_SceneGraph *sg;
     992             :                 /*apply event cascade - this is needed for cases where several events are processed between
     993             :                 2 simulation tick. If we don't flush the routes stack, the result will likely be wrong
     994             :                 */
     995        2144 :                 gf_sg_activate_routes(compositor->scene);
     996        2144 :                 i = 0;
     997        4303 :                 while ((sg = (GF_SceneGraph*)gf_list_enum(compositor->extra_scenes, &i))) {
     998          15 :                         gf_sg_activate_routes(sg);
     999             :                 }
    1000             :                 return 1;
    1001             :         }
    1002             :         return GF_FALSE;
    1003             : }
    1004             : 
    1005             : 
    1006         160 : static Bool exec_vrml_key_event(GF_Compositor *compositor, GF_Node *node, GF_Event *ev, Bool is_focus_out)
    1007             : {
    1008             :         GF_SensorHandler *hdl = NULL;
    1009             :         GF_ChildNodeItem *child;
    1010             :         u32 ret = 0;
    1011         160 :         if (!node) node = compositor->focus_node;
    1012         160 :         if (!node) return GF_FALSE;
    1013             : 
    1014          36 :         switch (gf_node_get_tag(node)) {
    1015             :         case TAG_MPEG4_Text:
    1016             :                 return GF_FALSE;
    1017           0 :         case TAG_MPEG4_Layout:
    1018           0 :                 hdl = compositor_mpeg4_layout_get_sensor_handler(node);
    1019           0 :                 break;
    1020           0 :         case TAG_MPEG4_Anchor:
    1021           0 :                 hdl = compositor_mpeg4_get_sensor_handler(node);
    1022           0 :                 break;
    1023             : #ifndef GPAC_DISABLE_X3D
    1024             :         case TAG_X3D_Text:
    1025             :                 return GF_FALSE;
    1026           0 :         case TAG_X3D_Anchor:
    1027           0 :                 hdl = compositor_mpeg4_get_sensor_handler(node);
    1028           0 :                 break;
    1029             : #endif
    1030             :         }
    1031          36 :         child = ((GF_ParentNode*)node)->children;
    1032           0 :         if (hdl) {
    1033           0 :                 ret += hdl->OnUserEvent(hdl, is_focus_out ? GF_FALSE : GF_TRUE, GF_FALSE, ev, compositor);
    1034             :         } else {
    1035         163 :                 while (child) {
    1036         127 :                         hdl = compositor_mpeg4_get_sensor_handler(child->node);
    1037         127 :                         if (hdl) {
    1038          36 :                                 ret += hdl->OnUserEvent(hdl, is_focus_out ? GF_FALSE : GF_TRUE, GF_FALSE, ev, compositor);
    1039             :                         }
    1040         127 :                         child = child->next;
    1041             :                 }
    1042             :         }
    1043          36 :         return ret ? GF_TRUE : GF_FALSE;
    1044             : }
    1045             : 
    1046             : #endif /*GPAC_DISABLE_VRML*/
    1047             : 
    1048        3923 : Bool visual_execute_event(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children)
    1049             : {
    1050             :         Bool ret;
    1051             :         Bool reset_sel = GF_FALSE;
    1052             :         GF_List *temp_stack;
    1053        3923 :         GF_Compositor *compositor = visual->compositor;
    1054        3923 :         tr_state->traversing_mode = TRAVERSE_PICK;
    1055             : #ifndef GPAC_DISABLE_3D
    1056        3923 :         tr_state->layer3d = NULL;
    1057             : #endif
    1058             : 
    1059             :         /*preprocess text selection and edition*/
    1060        3923 :         if ((ev->type<GF_EVENT_MOUSEWHEEL) && (ev->mouse.button==GF_MOUSE_LEFT)) {
    1061        3921 :                 if (compositor->text_selection) {
    1062          30 :                         if (ev->type==GF_EVENT_MOUSEUP) {
    1063           9 :                                 if (compositor->store_text_state==GF_SC_TSEL_ACTIVE)
    1064           1 :                                         compositor->store_text_state = GF_SC_TSEL_FROZEN;
    1065             :                                 else {
    1066             :                                         reset_sel = GF_TRUE;
    1067             :                                 }
    1068             :                         }
    1069          21 :                         else if (ev->type==GF_EVENT_MOUSEDOWN) {
    1070             :                                 reset_sel = GF_TRUE;
    1071             :                         }
    1072        3891 :                 } else if (compositor->edited_text) {
    1073           0 :                         if (ev->type==GF_EVENT_MOUSEDOWN)
    1074             :                                 reset_sel = GF_TRUE;
    1075             :                 }
    1076        3921 :                 if (ev->type==GF_EVENT_MOUSEUP) {
    1077         136 :                         if (hit_node_editable(compositor, GF_FALSE)) {
    1078           0 :                                 compositor->text_selection = NULL;
    1079             :                                 exec_text_input(compositor, NULL);
    1080           0 :                                 return GF_TRUE;
    1081             :                         }
    1082             : #if 0
    1083             :                         else if (!compositor->focus_node) {
    1084             :                                 gf_sc_focus_switch_ring(compositor, 0, gf_sg_get_root_node(compositor->scene), 1);
    1085             :                         }
    1086             : #endif
    1087             :                 }
    1088        3921 :                 if (reset_sel) {
    1089           8 :                         flush_text_node_edit(compositor, GF_TRUE);
    1090             : 
    1091           8 :                         compositor->store_text_state = GF_SC_TSEL_RELEASED;
    1092           8 :                         compositor->text_selection = NULL;
    1093           8 :                         if (compositor->selected_text) gf_free(compositor->selected_text);
    1094           8 :                         compositor->selected_text = NULL;
    1095           8 :                         if (compositor->sel_buffer) gf_free(compositor->sel_buffer);
    1096           8 :                         compositor->sel_buffer = NULL;
    1097           8 :                         compositor->sel_buffer_alloc = 0;
    1098           8 :                         compositor->sel_buffer_len = 0;
    1099             : 
    1100           8 :                         gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
    1101           8 :                         compositor->text_edit_changed = GF_TRUE;
    1102        3913 :                 } else if (compositor->store_text_state == GF_SC_TSEL_RELEASED) {
    1103           5 :                         compositor->store_text_state = GF_SC_TSEL_NONE;
    1104             :                 }
    1105             :         }
    1106             : 
    1107             :         /*pick node*/
    1108        3923 :         compositor->hit_appear = NULL;
    1109        3923 :         compositor->hit_text = NULL;
    1110        3923 :         temp_stack = compositor->prev_hit_use_stack;
    1111        3923 :         compositor->prev_hit_use_stack = compositor->hit_use_stack;
    1112        3923 :         compositor->hit_use_stack = temp_stack;
    1113             : 
    1114        3923 :         tr_state->pick_x = ev->mouse.x;
    1115        3923 :         tr_state->pick_y = ev->mouse.y;
    1116             : 
    1117             : 
    1118             : #ifndef GPAC_DISABLE_3D
    1119        3923 :         if (visual->type_3d)
    1120         510 :                 visual_3d_pick_node(visual, tr_state, ev, children);
    1121             :         else
    1122             : #endif
    1123        3413 :                 visual_2d_pick_node(visual, tr_state, ev, children);
    1124             : 
    1125        3923 :         gf_list_reset(tr_state->vrml_sensors);
    1126             : 
    1127        3945 :         if (exec_text_selection(compositor, ev))
    1128             :                 return GF_TRUE;
    1129             : 
    1130        3902 :         if (compositor->hit_use_dom_events) {
    1131           7 :                 ret = exec_event_dom(compositor, ev);
    1132           7 :                 if (ret) return GF_TRUE;
    1133             :                 /*no vrml sensors above*/
    1134           7 :                 if (!gf_list_count(compositor->sensors) && !gf_list_count(compositor->previous_sensors))
    1135             :                         return GF_FALSE;
    1136             :         }
    1137             : #ifndef GPAC_DISABLE_VRML
    1138        3895 :         return gf_sc_exec_event_vrml(compositor, ev);
    1139             : #else
    1140             :         return 0;
    1141             : #endif
    1142             : }
    1143             : 
    1144           0 : u32 gf_sc_svg_focus_navigate(GF_Compositor *compositor, u32 key_code)
    1145             : {
    1146             : #ifndef GPAC_DISABLE_SVG
    1147             :         SVGAllAttributes atts;
    1148             :         GF_DOM_Event evt;
    1149             :         u32 ret = 0;
    1150             :         GF_Node *n;
    1151             :         SVG_Focus *focus = NULL;
    1152             : 
    1153             :         /*only for dom-based nodes*/
    1154           0 :         if (!compositor->focus_node || !compositor->focus_uses_dom_events) return 0;
    1155             : 
    1156             :         n=NULL;
    1157           0 :         gf_svg_flatten_attributes((SVG_Element *)compositor->focus_node, &atts);
    1158           0 :         switch (key_code) {
    1159           0 :         case GF_KEY_LEFT:
    1160           0 :                 focus = atts.nav_left;
    1161           0 :                 break;
    1162           0 :         case GF_KEY_RIGHT:
    1163           0 :                 focus = atts.nav_right;
    1164           0 :                 break;
    1165           0 :         case GF_KEY_UP:
    1166           0 :                 focus = atts.nav_up;
    1167           0 :                 break;
    1168           0 :         case GF_KEY_DOWN:
    1169           0 :                 focus = atts.nav_down;
    1170           0 :                 break;
    1171             :         default:
    1172             :                 return 0;
    1173             :         }
    1174           0 :         if (!focus) return 0;
    1175             : 
    1176           0 :         if (focus->type==SVG_FOCUS_SELF) return 0;
    1177           0 :         if (focus->type==SVG_FOCUS_AUTO) return 0;
    1178           0 :         if (!focus->target.target) {
    1179           0 :                 if (!focus->target.string) return 0;
    1180           0 :                 focus->target.target = gf_sg_find_node_by_name(compositor->scene, focus->target.string+1);
    1181             :         }
    1182           0 :         n = (GF_Node*)focus->target.target;
    1183             : 
    1184             :         ret = 0;
    1185           0 :         if (n != compositor->focus_node) {
    1186             :                 /*the event is already handled, even though no listeners may be present*/
    1187             :                 ret = 1;
    1188             :                 memset(&evt, 0, sizeof(GF_DOM_Event));
    1189           0 :                 evt.bubbles = 1;
    1190           0 :                 if (compositor->focus_node) {
    1191           0 :                         evt.type = GF_EVENT_FOCUSOUT;
    1192           0 :                         gf_dom_event_fire(compositor->focus_node, &evt);
    1193             :                 }
    1194           0 :                 if (n) {
    1195           0 :                         evt.relatedTarget = n;
    1196           0 :                         evt.type = GF_EVENT_FOCUSIN;
    1197           0 :                         gf_dom_event_fire(n, &evt);
    1198             :                 }
    1199           0 :                 compositor->focus_node = n;
    1200             :                 //invalidate in case we draw focus rect
    1201           0 :                 gf_sc_invalidate(compositor, NULL);
    1202             :         }
    1203             :         return ret;
    1204             : #else
    1205             :         return 0;
    1206             : #endif /*GPAC_DISABLE_SVG*/
    1207             : }
    1208             : 
    1209             : /*focus management*/
    1210             : #ifndef GPAC_DISABLE_SVG
    1211        2044 : static Bool is_focus_target(GF_Node *elt)
    1212             : {
    1213             :         u32 i, count;
    1214        2044 :         u32 tag = gf_node_get_tag(elt);
    1215        2044 :         switch (tag) {
    1216             :         case TAG_SVG_a:
    1217             :                 return GF_TRUE;
    1218             : 
    1219             : #ifndef GPAC_DISABLE_VRML
    1220             :         case TAG_MPEG4_Anchor:
    1221             :                 return GF_TRUE;
    1222             : #ifndef GPAC_DISABLE_X3D
    1223             :         case TAG_X3D_Anchor:
    1224             :                 return GF_TRUE;
    1225             : #endif
    1226             : #endif
    1227             : 
    1228             :         default:
    1229             :                 break;
    1230             :         }
    1231        2042 :         if (tag<=GF_NODE_FIRST_DOM_NODE_TAG) return GF_FALSE;
    1232             : 
    1233        2042 :         count = gf_dom_listener_count(elt);
    1234        2045 :         for (i=0; i<count; i++) {
    1235             :                 GF_FieldInfo info;
    1236           3 :                 GF_Node *l = gf_dom_listener_get(elt, i);
    1237           3 :                 if (gf_node_get_attribute_by_tag(l, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info)==GF_OK) {
    1238           3 :                         switch ( ((XMLEV_Event*)info.far_ptr)->type) {
    1239           0 :                         case GF_EVENT_FOCUSIN:
    1240             :                         case GF_EVENT_FOCUSOUT:
    1241             :                         case GF_EVENT_ACTIVATE:
    1242             :                         /*although this is not in the SVGT1.2 spec, we also enable focus switching if key events are listened on the element*/
    1243             :                         case GF_EVENT_KEYDOWN:
    1244             :                         case GF_EVENT_KEYUP:
    1245             :                         case GF_EVENT_LONGKEYPRESS:
    1246           0 :                                 return GF_TRUE;
    1247             :                         default:
    1248             :                                 break;
    1249             :                         }
    1250           0 :                 }
    1251             :         }
    1252             :         return GF_FALSE;
    1253             : }
    1254             : #endif /*GPAC_DISABLE_SVG*/
    1255             : 
    1256             : #define CALL_SET_FOCUS(__child) {       \
    1257             :         gf_list_add(compositor->focus_ancestors, elt);       \
    1258             :         n = set_focus(compositor, __child, current_focus, prev_focus);  \
    1259             :         if (n) {        \
    1260             :                 gf_node_set_cyclic_traverse_flag(elt, 0);       \
    1261             :                 return n;       \
    1262             :         }       \
    1263             :         gf_list_rem_last(compositor->focus_ancestors);       \
    1264             :         gf_node_set_cyclic_traverse_flag(elt, 0);\
    1265             :         return NULL;    \
    1266             :         }       \
    1267             :  
    1268             : #ifndef GPAC_DISABLE_SVG
    1269           0 : static void rebuild_focus_ancestor(GF_Compositor *compositor, GF_Node *elt)
    1270             : {
    1271           0 :         gf_list_reset(compositor->focus_ancestors);
    1272           0 :         while (elt) {
    1273           0 :                 GF_Node *par = gf_node_get_parent(elt, 0);
    1274           0 :                 if (!par) break;
    1275           0 :                 gf_list_insert(compositor->focus_ancestors, par, 0);
    1276             :                 elt = par;
    1277             :         }
    1278           0 : }
    1279             : #endif // GPAC_DISABLE_SVG
    1280             : 
    1281        2395 : static GF_Node *set_focus(GF_Compositor *compositor, GF_Node *elt, Bool current_focus, Bool prev_focus)
    1282             : {
    1283             :         u32 tag;
    1284             :         GF_ChildNodeItem *child = NULL;
    1285             :         GF_Node *use_node = NULL;
    1286             :         GF_Node *anim_node = NULL;
    1287             :         GF_Node *n;
    1288             : 
    1289        2395 :         if (!elt) return NULL;
    1290             : 
    1291             :         /*all return in this function shall be preceeded with gf_node_set_cyclic_traverse_flag(elt, 0)
    1292             :         this ensures that we don't go into cyclic references when moving focus, hence stack overflow*/
    1293        2394 :         if (! gf_node_set_cyclic_traverse_flag(elt, GF_TRUE)) return NULL;
    1294             : 
    1295        2394 :         tag = gf_node_get_tag(elt);
    1296        2394 :         if (tag <= GF_NODE_FIRST_DOM_NODE_TAG) {
    1297         346 :                 switch (tag) {
    1298             : #ifndef GPAC_DISABLE_VRML
    1299           0 :                 case TAG_MPEG4_Transform:
    1300             :                 {
    1301             :                         M_Transform *tr=(M_Transform *)elt;
    1302           0 :                         if (!tr->scale.x || !tr->scale.y || !tr->scale.z) {
    1303           0 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1304           0 :                                 return NULL;
    1305             :                         }
    1306             :                         goto test_grouping;
    1307             :                 }
    1308          34 :                 case TAG_MPEG4_Transform2D:
    1309             :                 {
    1310             :                         M_Transform2D *tr=(M_Transform2D *)elt;
    1311          34 :                         if (!tr->scale.x || !tr->scale.y) {
    1312           2 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1313           2 :                                 return NULL;
    1314             :                         }
    1315             :                         goto test_grouping;
    1316             :                 }
    1317           0 :                 case TAG_MPEG4_Layer3D:
    1318             :                 case TAG_MPEG4_Layer2D:
    1319             :                 {
    1320             :                         M_Layer2D *l=(M_Layer2D *)elt;
    1321           0 :                         if (!l->size.x || !l->size.y) {
    1322           0 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1323           0 :                                 return NULL;
    1324             :                         }
    1325             :                         goto test_grouping;
    1326             :                 }
    1327             :                 case TAG_MPEG4_Form:
    1328             :                 case TAG_MPEG4_TransformMatrix2D:
    1329             :                 case TAG_MPEG4_Group:
    1330             :                 case TAG_MPEG4_Billboard:
    1331             :                 case TAG_MPEG4_Collision:
    1332             :                 case TAG_MPEG4_LOD:
    1333             :                 case TAG_MPEG4_OrderedGroup:
    1334             :                 case TAG_MPEG4_ColorTransform:
    1335             :                 case TAG_MPEG4_PathLayout:
    1336             :                 case TAG_MPEG4_Anchor:
    1337             : #ifndef GPAC_DISABLE_X3D
    1338             :                 case TAG_X3D_Group:
    1339             :                 case TAG_X3D_Transform:
    1340             :                 case TAG_X3D_Billboard:
    1341             :                 case TAG_X3D_Collision:
    1342             :                 case TAG_X3D_LOD:
    1343             :                 case TAG_X3D_Anchor:
    1344             : #endif
    1345             : 
    1346          32 : test_grouping:
    1347          32 :                         if (!current_focus) {
    1348             :                                 /*get the base grouping stack (*/
    1349          32 :                                 BaseGroupingStack *grp = (BaseGroupingStack*)gf_node_get_private(elt);
    1350          32 :                                 if (grp && (grp->flags & (GROUP_HAS_SENSORS | GROUP_IS_ANCHOR) )) {
    1351          16 :                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1352          16 :                                         return elt;
    1353             :                                 }
    1354             :                         }
    1355             :                         break;
    1356           0 :                 case TAG_MPEG4_Switch:
    1357             : #ifndef GPAC_DISABLE_X3D
    1358             :                 case TAG_X3D_Switch:
    1359             : #endif
    1360             :                 {
    1361             :                         s32 i, wc;
    1362             : #ifndef GPAC_DISABLE_X3D
    1363           0 :                         if (tag==TAG_X3D_Switch) {
    1364           0 :                                 child = ((X_Switch*)elt)->children;
    1365           0 :                                 wc = ((X_Switch*)elt)->whichChoice;
    1366             :                         } else
    1367             : #endif
    1368             :                         {
    1369           0 :                                 child = ((M_Switch*)elt)->choice;
    1370           0 :                                 wc = ((M_Switch*)elt)->whichChoice;
    1371             :                         }
    1372           0 :                         if (wc==-1) {
    1373           0 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1374           0 :                                 return NULL;
    1375             :                         }
    1376             :                         i=0;
    1377           0 :                         while (child) {
    1378           0 :                                 if (i==wc) CALL_SET_FOCUS(child->node);
    1379           0 :                                 child = child->next;
    1380             :                         }
    1381           0 :                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1382           0 :                         return NULL;
    1383             :                 }
    1384             : 
    1385           1 :                 case TAG_MPEG4_Text:
    1386             : #ifndef GPAC_DISABLE_X3D
    1387             :                 case TAG_X3D_Text:
    1388             : #endif
    1389           1 :                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1390             : 
    1391           1 :                         if (!current_focus) {
    1392           1 :                                 M_FontStyle *fs = (M_FontStyle *) ((M_Text *)elt)->fontStyle;
    1393             : 
    1394           1 :                                 if (!fs || !fs->style.buffer) return NULL;
    1395             : 
    1396           1 :                                 if (strstr(fs->style.buffer, "editable") || strstr(fs->style.buffer, "EDITABLE")) {
    1397           0 :                                         compositor->focus_text_type = 3;
    1398           1 :                                 } else if (strstr(fs->style.buffer, "simple_edit") || strstr(fs->style.buffer, "SIMPLE_EDIT")) {
    1399           0 :                                         compositor->focus_text_type = 4;
    1400             :                                 } else {
    1401             :                                         return NULL;
    1402             :                                 }
    1403             :                                 return elt;
    1404             :                         }
    1405             :                         return NULL;
    1406           0 :                 case TAG_MPEG4_Layout:
    1407             :                 {
    1408             :                         M_Layout *l=(M_Layout*)elt;
    1409           0 :                         if (!l->size.x || !l->size.y) {
    1410           0 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1411           0 :                                 return NULL;
    1412             :                         }
    1413           0 :                         if (!current_focus && (compositor_mpeg4_layout_get_sensor_handler(elt)!=NULL)) {
    1414           0 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1415           0 :                                 return elt;
    1416             :                         }
    1417             :                 }
    1418             :                 break;
    1419             : 
    1420           0 :                 case TAG_ProtoNode:
    1421             :                         /*hardcoded proto acting as a grouping node*/
    1422           0 :                         if (gf_node_proto_is_grouping(elt)) {
    1423             :                                 GF_FieldInfo info;
    1424           0 :                                 if (!current_focus) {
    1425             :                                         /*get the base grouping stack (*/
    1426           0 :                                         BaseGroupingStack *grp = (BaseGroupingStack*)gf_node_get_private(elt);
    1427           0 :                                         if (grp && (grp->flags & (GROUP_HAS_SENSORS | GROUP_IS_ANCHOR) )) {
    1428           0 :                                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1429           0 :                                                 return elt;
    1430             :                                         }
    1431             :                                 }
    1432           0 :                                 if ( (gf_node_get_field_by_name(elt, "children", &info) != GF_OK) || (info.fieldType != GF_SG_VRML_MFNODE)) {
    1433           0 :                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1434           0 :                                         return NULL;
    1435             :                                 }
    1436           0 :                                 child = *(GF_ChildNodeItem **) info.far_ptr;
    1437             :                         } else {
    1438           0 :                                 CALL_SET_FOCUS(gf_node_get_proto_root(elt));
    1439             :                         }
    1440             :                         break;
    1441             : 
    1442           0 :                 case TAG_MPEG4_Inline:
    1443             : #ifndef GPAC_DISABLE_X3D
    1444             :                 case TAG_X3D_Inline:
    1445             : #endif
    1446           0 :                         CALL_SET_FOCUS(gf_scene_get_subscene_root(elt));
    1447             : 
    1448          15 :                 case TAG_MPEG4_Shape:
    1449             : #ifndef GPAC_DISABLE_X3D
    1450             :                 case TAG_X3D_Shape:
    1451             : #endif
    1452             : 
    1453          15 :                         gf_list_add(compositor->focus_ancestors, elt);
    1454          15 :                         n = set_focus(compositor, ((M_Shape*)elt)->geometry, current_focus, prev_focus);
    1455          15 :                         if (!n) n = set_focus(compositor, ((M_Shape*)elt)->appearance, current_focus, prev_focus);
    1456          15 :                         if (n) {
    1457          12 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1458          12 :                                 return n;
    1459             :                         }
    1460           3 :                         gf_list_rem_last(compositor->focus_ancestors);
    1461           3 :                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1462           3 :                         return NULL;
    1463             : 
    1464          15 :                 case TAG_MPEG4_Appearance:
    1465             : #ifndef GPAC_DISABLE_X3D
    1466             :                 case TAG_X3D_Appearance:
    1467             : #endif
    1468          15 :                         CALL_SET_FOCUS(((M_Appearance*)elt)->texture);
    1469             : 
    1470          13 :                 case TAG_MPEG4_CompositeTexture2D:
    1471             :                 case TAG_MPEG4_CompositeTexture3D:
    1472             :                         /*CompositeTextures are not grouping nodes per say*/
    1473          13 :                         child = ((GF_ParentNode*)elt)->children;
    1474          40 :                         while (child) {
    1475          14 :                                 if (compositor_mpeg4_get_sensor_handler(child->node) != NULL) {
    1476           0 :                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1477           0 :                                         return elt;
    1478             :                                 }
    1479          14 :                                 child = child->next;
    1480             :                         }
    1481             :                         break;
    1482             : 
    1483             : #endif /*GPAC_DISABLE_VRML*/
    1484             : 
    1485         268 :                 default:
    1486         268 :                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1487         268 :                         return NULL;
    1488             :                 }
    1489          13 :                 if (!child)
    1490          29 :                         child = ((GF_ParentNode*)elt)->children;
    1491             :         } else {
    1492             : #ifndef GPAC_DISABLE_SVG
    1493             :                 SVGAllAttributes atts;
    1494             : 
    1495        2048 :                 if (tag==TAG_SVG_defs) {
    1496           3 :                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1497           3 :                         return NULL;
    1498             :                 }
    1499        2045 :                 gf_svg_flatten_attributes((SVG_Element *)elt, &atts);
    1500             : 
    1501        2045 :                 if (atts.display && (*atts.display==SVG_DISPLAY_NONE)) {
    1502           0 :                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1503           0 :                         return NULL;
    1504             :                 }
    1505        2045 :                 if (!current_focus) {
    1506             :                         Bool is_auto = GF_TRUE;
    1507        2045 :                         if (atts.focusable) {
    1508           1 :                                 if (*atts.focusable==SVG_FOCUSABLE_TRUE) {
    1509           0 :                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1510           0 :                                         return elt;
    1511             :                                 }
    1512           1 :                                 if (*atts.focusable==SVG_FOCUSABLE_FALSE) is_auto = GF_FALSE;
    1513             :                         }
    1514        2044 :                         if (is_auto && is_focus_target(elt)) {
    1515           2 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1516           2 :                                 return elt;
    1517             :                         }
    1518             : 
    1519        2043 :                         if (atts.editable && *atts.editable) {
    1520           0 :                                 switch (tag) {
    1521           0 :                                 case TAG_SVG_text:
    1522             :                                 case TAG_SVG_textArea:
    1523           0 :                                         compositor->focus_text_type = 1;
    1524           0 :                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1525           0 :                                         return elt;
    1526           0 :                                 case TAG_SVG_tspan:
    1527           0 :                                         compositor->focus_text_type = 2;
    1528           0 :                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1529           0 :                                         return elt;
    1530             :                                 default:
    1531             :                                         break;
    1532             :                                 }
    1533        2043 :                         }
    1534             :                 }
    1535             : 
    1536        2043 :                 if (prev_focus) {
    1537           0 :                         if (atts.nav_prev) {
    1538           0 :                                 switch (atts.nav_prev->type) {
    1539           0 :                                 case SVG_FOCUS_SELF:
    1540             :                                         /*focus locked on element*/
    1541           0 :                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1542           0 :                                         return elt;
    1543           0 :                                 case SVG_FOCUS_IRI:
    1544           0 :                                         if (!atts.nav_prev->target.target) {
    1545           0 :                                                 if (!atts.nav_prev->target.string) {
    1546           0 :                                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1547           0 :                                                         return NULL;
    1548             :                                                 }
    1549           0 :                                                 atts.nav_prev->target.target = gf_sg_find_node_by_name(compositor->scene, atts.nav_prev->target.string+1);
    1550             :                                         }
    1551           0 :                                         if (atts.nav_prev->target.target) {
    1552           0 :                                                 rebuild_focus_ancestor(compositor, atts.nav_prev->target.target);
    1553           0 :                                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1554           0 :                                                 return atts.nav_prev->target.target;
    1555             :                                         }
    1556             :                                 default:
    1557             :                                         break;
    1558             :                                 }
    1559           0 :                         }
    1560             :                 } else {
    1561             :                         /*check next*/
    1562        2043 :                         if (atts.nav_next) {
    1563           0 :                                 switch (atts.nav_next->type) {
    1564           0 :                                 case SVG_FOCUS_SELF:
    1565             :                                         /*focus locked on element*/
    1566           0 :                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1567           0 :                                         return elt;
    1568           0 :                                 case SVG_FOCUS_IRI:
    1569           0 :                                         if (!atts.nav_next->target.target) {
    1570           0 :                                                 if (!atts.nav_next->target.string) {
    1571           0 :                                                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1572           0 :                                                         return NULL;
    1573             :                                                 }
    1574           0 :                                                 atts.nav_next->target.target = gf_sg_find_node_by_name(compositor->scene, atts.nav_next->target.string+1);
    1575             :                                         }
    1576           0 :                                         if (atts.nav_next->target.target) {
    1577           0 :                                                 rebuild_focus_ancestor(compositor, atts.nav_next->target.target);
    1578           0 :                                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1579           0 :                                                 return atts.nav_next->target.target;
    1580             :                                         }
    1581             :                                 default:
    1582             :                                         break;
    1583             :                                 }
    1584        2043 :                         }
    1585             :                 }
    1586        2043 :                 if (atts.xlink_href) {
    1587          56 :                         switch (tag) {
    1588          13 :                         case TAG_SVG_use:
    1589          13 :                                 use_node = compositor_svg_get_xlink_resource_node(elt, atts.xlink_href);
    1590          13 :                                 break;
    1591           7 :                         case TAG_SVG_animation:
    1592           7 :                                 anim_node = compositor_svg_get_xlink_resource_node(elt, atts.xlink_href);
    1593           7 :                                 break;
    1594             :                         }
    1595             :                 }
    1596             : #endif /*GPAC_DISABLE_SVG*/
    1597        2043 :                 child = ((GF_ParentNode *)elt)->children;
    1598             :         }
    1599             : 
    1600        2072 :         if (prev_focus) {
    1601             :                 u32 count, i;
    1602             :                 /*check all children except if current focus*/
    1603           0 :                 if (current_focus) {
    1604           0 :                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1605           0 :                         return NULL;
    1606             :                 }
    1607           0 :                 gf_list_add(compositor->focus_ancestors, elt);
    1608           0 :                 count = gf_node_list_get_count(child);
    1609           0 :                 for (i=count; i>0; i--) {
    1610             :                         /*get in the subtree*/
    1611           0 :                         n = gf_node_list_get_child( child, i-1);
    1612           0 :                         n = set_focus(compositor, n, GF_FALSE, GF_TRUE);
    1613           0 :                         if (n) {
    1614           0 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1615           0 :                                 return n;
    1616             :                         }
    1617             :                 }
    1618             :         } else {
    1619             :                 /*check all children */
    1620        2072 :                 gf_list_add(compositor->focus_ancestors, elt);
    1621        6410 :                 while (child) {
    1622             :                         /*get in the subtree*/
    1623        2292 :                         n = set_focus(compositor, child->node, GF_FALSE, GF_FALSE);
    1624        2292 :                         if (n) {
    1625          26 :                                 gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1626          26 :                                 return n;
    1627             :                         }
    1628        2266 :                         child = child->next;
    1629             :                 }
    1630             :         }
    1631        2046 :         if (use_node) {
    1632          11 :                 gf_list_add(compositor->focus_use_stack, use_node);
    1633          11 :                 gf_list_add(compositor->focus_use_stack, elt);
    1634          11 :                 n = set_focus(compositor, use_node, GF_FALSE, GF_FALSE);
    1635          11 :                 if (n) {
    1636           0 :                         compositor->focus_used = elt;
    1637           0 :                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1638           0 :                         return n;
    1639             :                 }
    1640          11 :                 gf_list_rem_last(compositor->focus_use_stack);
    1641          11 :                 gf_list_rem_last(compositor->focus_use_stack);
    1642        2035 :         } else if (anim_node) {
    1643           0 :                 n = set_focus(compositor, anim_node, GF_FALSE, GF_FALSE);
    1644           0 :                 if (n) {
    1645           0 :                         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1646           0 :                         return n;
    1647             :                 }
    1648             :         }
    1649             : 
    1650        2046 :         gf_list_rem_last(compositor->focus_ancestors);
    1651        2046 :         gf_node_set_cyclic_traverse_flag(elt, GF_FALSE);
    1652        2046 :         return NULL;
    1653             : }
    1654             : 
    1655          29 : static GF_Node *browse_parent_for_focus(GF_Compositor *compositor, GF_Node *elt, Bool prev_focus)
    1656             : {
    1657             :         u32 tag;
    1658             :         s32 idx = 0;
    1659             :         GF_ChildNodeItem *child;
    1660             :         GF_Node *n;
    1661             :         GF_Node *par;
    1662             : 
    1663          29 :         par = (GF_Node*)gf_list_last(compositor->focus_ancestors);
    1664          29 :         if (!par) return NULL;
    1665             : 
    1666           0 :         tag = gf_node_get_tag(par);
    1667           0 :         if (tag <= GF_NODE_FIRST_DOM_NODE_TAG) {
    1668           0 :                 switch (tag) {
    1669             : #ifndef GPAC_DISABLE_VRML
    1670           0 :                 case TAG_MPEG4_Group:
    1671             :                 case TAG_MPEG4_Transform:
    1672             :                 case TAG_MPEG4_Billboard:
    1673             :                 case TAG_MPEG4_Collision:
    1674             :                 case TAG_MPEG4_LOD:
    1675             :                 case TAG_MPEG4_OrderedGroup:
    1676             :                 case TAG_MPEG4_Transform2D:
    1677             :                 case TAG_MPEG4_TransformMatrix2D:
    1678             :                 case TAG_MPEG4_ColorTransform:
    1679             :                 case TAG_MPEG4_Layer3D:
    1680             :                 case TAG_MPEG4_Layer2D:
    1681             :                 case TAG_MPEG4_Layout:
    1682             :                 case TAG_MPEG4_PathLayout:
    1683             :                 case TAG_MPEG4_Form:
    1684             :                 case TAG_MPEG4_Anchor:
    1685             : #ifndef GPAC_DISABLE_X3D
    1686             :                 case TAG_X3D_Anchor:
    1687             :                 case TAG_X3D_Group:
    1688             :                 case TAG_X3D_Transform:
    1689             :                 case TAG_X3D_Billboard:
    1690             :                 case TAG_X3D_Collision:
    1691             :                 case TAG_X3D_LOD:
    1692             : #endif
    1693             :                 case TAG_MPEG4_CompositeTexture2D:
    1694             :                 case TAG_MPEG4_CompositeTexture3D:
    1695           0 :                         child = ((GF_ParentNode*)par)->children;
    1696           0 :                         break;
    1697           0 :                 case TAG_ProtoNode:
    1698             :                         /*hardcoded proto acting as a grouping node*/
    1699           0 :                         if (gf_node_proto_is_grouping(par)) {
    1700             :                                 GF_FieldInfo info;
    1701           0 :                                 if ((gf_node_get_field_by_name(par, "children", &info) == GF_OK)
    1702           0 :                                         && (info.fieldType == GF_SG_VRML_MFNODE)
    1703             :                                    ) {
    1704           0 :                                         child = *(GF_ChildNodeItem **) info.far_ptr;
    1705           0 :                                         break;
    1706             :                                 }
    1707             :                         }
    1708             :                         /*fall through*/
    1709             : 
    1710             : #endif  /*GPAC_DISABLE_VRML*/
    1711             : 
    1712             :                 /*for all other node, locate parent*/
    1713             :                 default:
    1714           0 :                         gf_list_rem_last(compositor->focus_ancestors);
    1715           0 :                         return browse_parent_for_focus(compositor, par, prev_focus);
    1716             :                 }
    1717             :         } else {
    1718           0 :                 child = ((GF_ParentNode *)par)->children;
    1719             :         }
    1720             : 
    1721             :         /*locate element*/
    1722           0 :         idx = gf_node_list_find_child(child, elt);
    1723             : 
    1724             :         /*use or animation*/
    1725           0 :         if (idx<0) {
    1726             :                 /*up one level*/
    1727           0 :                 gf_list_rem_last(compositor->focus_ancestors);
    1728             : #ifndef GPAC_DISABLE_SVG
    1729           0 :                 if (tag==TAG_SVG_use) {
    1730           0 :                         gf_list_rem_last(compositor->focus_use_stack);
    1731           0 :                         gf_list_rem_last(compositor->focus_use_stack);
    1732           0 :                         if (compositor->focus_used == par) compositor->focus_used = NULL;
    1733             :                 }
    1734             : #endif
    1735           0 :                 return browse_parent_for_focus(compositor, (GF_Node*)par, prev_focus);
    1736             :         }
    1737             : 
    1738           0 :         if (prev_focus) {
    1739             :                 u32 i;
    1740             :                 /*!! this may happen when walking up PROTO nodes !!*/
    1741           0 :                 for (i=idx; i>0; i--) {
    1742           0 :                         n = gf_node_list_get_child(child, i-1);
    1743             :                         /*get in the subtree*/
    1744           0 :                         n = set_focus(compositor, n, GF_FALSE, GF_TRUE);
    1745           0 :                         if (n) return n;
    1746             :                 }
    1747             :         } else {
    1748             :                 /*!! this may happen when walking up PROTO nodes !!*/
    1749           0 :                 while (child) {
    1750           0 :                         if (idx<0) {
    1751             :                                 /*get in the subtree*/
    1752           0 :                                 n = set_focus(compositor, child->node, GF_FALSE, GF_FALSE);
    1753           0 :                                 if (n) return n;
    1754             :                         }
    1755           0 :                         idx--;
    1756           0 :                         child = child->next;
    1757             :                 }
    1758             :         }
    1759             :         /*up one level*/
    1760           0 :         gf_list_rem_last(compositor->focus_ancestors);
    1761           0 :         return browse_parent_for_focus(compositor, (GF_Node*)par, prev_focus);
    1762             : }
    1763             : 
    1764             : GF_EXPORT
    1765          68 : u32 gf_sc_focus_switch_ring(GF_Compositor *compositor, Bool move_prev, GF_Node *focus, u32 force_focus)
    1766             : {
    1767             :         Bool current_focus = GF_TRUE;
    1768             : #ifndef GPAC_DISABLE_SVG
    1769             :         Bool prev_uses_dom_events;
    1770             :         GF_Node *prev_use;
    1771             : #endif
    1772             :         u32 ret = 0;
    1773             :         GF_List *cloned_use = NULL;
    1774             :         GF_Node *n, *prev;
    1775             : 
    1776          68 :         compositor->focus_text_type = 0;
    1777          68 :         prev = compositor->focus_node;
    1778             : #ifndef GPAC_DISABLE_SVG
    1779          68 :         prev_use = compositor->focus_used;
    1780          68 :         prev_uses_dom_events = compositor->focus_uses_dom_events;
    1781             : #endif
    1782          68 :         compositor->focus_uses_dom_events = GF_FALSE;
    1783             : 
    1784          68 :         if (!compositor->focus_node) {
    1785          35 :                 compositor->focus_node = (force_focus==2) ? focus : gf_sg_get_root_node(compositor->scene);
    1786          35 :                 gf_list_reset(compositor->focus_ancestors);
    1787          35 :                 if (!compositor->focus_node) return 0;
    1788             :                 current_focus = GF_FALSE;
    1789             :         }
    1790             : 
    1791          68 :         if (compositor->focus_used) {
    1792             :                 u32 i, count;
    1793           0 :                 cloned_use = gf_list_new();
    1794           0 :                 count = gf_list_count(compositor->focus_use_stack);
    1795           0 :                 for (i=0; i<count; i++) {
    1796           0 :                         gf_list_add(cloned_use, gf_list_get(compositor->focus_use_stack, i));
    1797             :                 }
    1798             :         }
    1799             : 
    1800             :         /*get focus in current doc order*/
    1801          68 :         if (force_focus) {
    1802          40 :                 gf_list_reset(compositor->focus_ancestors);
    1803             :                 n = focus;
    1804          40 :                 if (force_focus==2) {
    1805             :                         current_focus = GF_FALSE;
    1806          19 :                         n = set_focus(compositor, focus, current_focus, move_prev);
    1807          19 :                         if (!n) n = browse_parent_for_focus(compositor, focus, move_prev);
    1808             :                 }
    1809             :         } else {
    1810          28 :                 n = set_focus(compositor, compositor->focus_node, current_focus, move_prev);
    1811          28 :                 if (!n) n = browse_parent_for_focus(compositor, compositor->focus_node, move_prev);
    1812             : 
    1813          28 :                 if (!n) {
    1814          26 :                         if (!prev) n = gf_sg_get_root_node(compositor->scene);
    1815          26 :                         gf_list_reset(compositor->focus_ancestors);
    1816             :                 }
    1817             :         }
    1818             : 
    1819          68 :         if (n && gf_node_get_tag(n)>=GF_NODE_FIRST_DOM_NODE_TAG) {
    1820          28 :                 compositor->focus_uses_dom_events = GF_TRUE;
    1821             :         }
    1822          68 :         compositor->focus_node = n;
    1823             : 
    1824             :         ret = 0;
    1825             : #ifndef GPAC_DISABLE_SVG
    1826          68 :         if ((prev != compositor->focus_node) || (prev_use != compositor->focus_used)) {
    1827             :                 GF_DOM_Event evt;
    1828             :                 GF_Event ev;
    1829             :                 memset(&evt, 0, sizeof(GF_DOM_Event));
    1830             :                 memset(&ev, 0, sizeof(GF_Event));
    1831          50 :                 ev.type = GF_EVENT_KEYDOWN;
    1832          50 :                 ev.key.key_code = move_prev ? GF_KEY_LEFT : GF_KEY_RIGHT;
    1833             : 
    1834          50 :                 if (prev) {
    1835          16 :                         if (prev_uses_dom_events) {
    1836           0 :                                 evt.bubbles = 1;
    1837           0 :                                 evt.type = GF_EVENT_FOCUSOUT;
    1838           0 :                                 gf_dom_event_fire_ex(prev, &evt, cloned_use);
    1839             :                         }
    1840             : #ifndef GPAC_DISABLE_VRML
    1841             :                         else {
    1842          16 :                                 exec_vrml_key_event(compositor, prev, &ev, GF_TRUE);
    1843             :                         }
    1844             : #endif
    1845             :                 }
    1846          50 :                 if (compositor->focus_node) {
    1847             :                         /*the event is already handled, even though no listeners may be present*/
    1848             :                         ret = 1;
    1849          48 :                         if (compositor->focus_uses_dom_events) {
    1850          28 :                                 evt.bubbles = 1;
    1851          28 :                                 evt.type = GF_EVENT_FOCUSIN;
    1852          28 :                                 gf_dom_event_fire_ex(compositor->focus_node, &evt, compositor->focus_use_stack);
    1853             :                         }
    1854             : #ifndef GPAC_DISABLE_VRML
    1855             :                         else {
    1856          20 :                                 exec_vrml_key_event(compositor, NULL, &ev, GF_FALSE);
    1857             :                         }
    1858             : #endif
    1859             :                 }
    1860             :                 /*because of offscreen caches and composite texture, we must invalidate subtrees of previous and new focus to force a redraw*/
    1861          50 :                 if (prev) gf_node_dirty_set(prev, GF_SG_NODE_DIRTY, GF_TRUE);
    1862          50 :                 if (compositor->focus_node) gf_node_dirty_set(compositor->focus_node, GF_SG_NODE_DIRTY, GF_TRUE);
    1863             :                 /*invalidate in case we draw focus rect*/
    1864          50 :                 gf_sc_invalidate(compositor, NULL);
    1865             :         }
    1866             : #endif /*GPAC_DISABLE_SVG*/
    1867          68 :         if (cloned_use) gf_list_del(cloned_use);
    1868             : 
    1869          68 :         if (hit_node_editable(compositor, GF_TRUE)) {
    1870           0 :                 compositor->text_selection = NULL;
    1871             :                 exec_text_input(compositor, NULL);
    1872             :                 /*invalidate parent graphs*/
    1873           0 :                 gf_node_dirty_set(compositor->focus_node, GF_SG_NODE_DIRTY, GF_TRUE);
    1874             :         }
    1875             :         return ret;
    1876             : }
    1877             : 
    1878        3979 : Bool gf_sc_execute_event(GF_Compositor *compositor, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children)
    1879             : {
    1880             :         /*filter mouse events and other events (key...)*/
    1881        3979 :         if (ev->type>GF_EVENT_MOUSEWHEEL) {
    1882             :                 Bool ret = GF_FALSE;
    1883             :                 /*send text edit*/
    1884         124 :                 if (compositor->edited_text) {
    1885           0 :                         exec_text_input(compositor, ev);
    1886           0 :                         if (compositor->edited_text) return GF_TRUE;
    1887             :                         /*if text is no longer edited, this is focus change so process as usual*/
    1888             :                 }
    1889             :                 /*FIXME - this is not working for mixed docs*/
    1890         124 :                 if (compositor->focus_uses_dom_events)
    1891           0 :                         ret = exec_event_dom(compositor, ev);
    1892             : #ifndef GPAC_DISABLE_VRML
    1893             :                 else
    1894         124 :                         ret = exec_vrml_key_event(compositor, compositor->focus_node, ev, GF_FALSE);
    1895             : #endif
    1896             : 
    1897         124 :                 if (ev->type==GF_EVENT_KEYDOWN) {
    1898          82 :                         switch (ev->key.key_code) {
    1899             :                         case GF_KEY_ENTER:
    1900             :                                 if ((0) && compositor->focus_text_type) {
    1901             :                                         exec_text_input(compositor, NULL);
    1902             :                                         ret = GF_TRUE;
    1903             : #ifndef GPAC_DISABLE_VRML
    1904           1 :                                 } else if (compositor->keynav_node && ((M_KeyNavigator*)compositor->keynav_node)->select) {
    1905           0 :                                         gf_sc_change_key_navigator(compositor, ((M_KeyNavigator*)compositor->keynav_node)->select);
    1906             :                                         ret = GF_TRUE;
    1907             : #endif
    1908             :                                 }
    1909             :                                 break;
    1910           0 :                         case GF_KEY_TAB:
    1911           0 :                                 ret += gf_sc_focus_switch_ring(compositor, (ev->key.flags & GF_KEY_MOD_SHIFT) ? 1 : 0, NULL, 0);
    1912           0 :                                 break;
    1913          53 :                         case GF_KEY_UP:
    1914             :                         case GF_KEY_DOWN:
    1915             :                         case GF_KEY_LEFT:
    1916             :                         case GF_KEY_RIGHT:
    1917          53 :                                 if (compositor->focus_uses_dom_events) {
    1918           0 :                                         ret += gf_sc_svg_focus_navigate(compositor, ev->key.key_code);
    1919             :                                 }
    1920             : #ifndef GPAC_DISABLE_VRML
    1921          53 :                                 else if (compositor->keynav_node) {
    1922             :                                         GF_Node *next_nav = NULL;
    1923           0 :                                         switch (ev->key.key_code) {
    1924           0 :                                         case GF_KEY_UP:
    1925           0 :                                                 next_nav = ((M_KeyNavigator*)compositor->keynav_node)->up;
    1926           0 :                                                 break;
    1927           0 :                                         case GF_KEY_DOWN:
    1928           0 :                                                 next_nav = ((M_KeyNavigator*)compositor->keynav_node)->down;
    1929           0 :                                                 break;
    1930           0 :                                         case GF_KEY_LEFT:
    1931           0 :                                                 next_nav = ((M_KeyNavigator*)compositor->keynav_node)->left;
    1932           0 :                                                 break;
    1933           0 :                                         case GF_KEY_RIGHT:
    1934           0 :                                                 next_nav = ((M_KeyNavigator*)compositor->keynav_node)->right;
    1935           0 :                                                 break;
    1936             :                                         }
    1937           0 :                                         if (next_nav) {
    1938           0 :                                                 gf_sc_change_key_navigator(compositor, next_nav);
    1939             :                                                 ret = GF_TRUE;
    1940             :                                         }
    1941             :                                 }
    1942             : #endif
    1943             :                                 break;
    1944             :                         }
    1945             :                 }
    1946             :                 return ret;
    1947             :         }
    1948             :         /*pick even, call visual handler*/
    1949        3855 :         return visual_execute_event(compositor->visual, tr_state, ev, children);
    1950             : }
    1951             : 
    1952        4049 : static Bool forward_event(GF_Compositor *compositor, GF_Event *ev, Bool consumed)
    1953             : {
    1954        4049 :         Bool ret = gf_filter_forward_gf_event(compositor->filter, ev, consumed, GF_FALSE);
    1955             : 
    1956        4049 :         if (consumed) return GF_FALSE;
    1957             : 
    1958        1885 :         if ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) {
    1959             :                 u32 now;
    1960             :                 GF_Event event;
    1961             :                 /*emulate doubleclick unless in step mode*/
    1962          18 :                 now = gf_sys_clock();
    1963          18 :                 if (!compositor->step_mode && (now - compositor->last_click_time < DOUBLECLICK_TIME_MS)) {
    1964          17 :                         event.type = GF_EVENT_DBLCLICK;
    1965          17 :                         event.mouse.key_states = compositor->key_states;
    1966          17 :                         event.mouse.x = ev->mouse.x;
    1967          17 :                         event.mouse.y = ev->mouse.y;
    1968          17 :                         ret += gf_sc_send_event(compositor, &event);
    1969             :                 }
    1970          18 :                 compositor->last_click_time = now;
    1971             :         }
    1972        1885 :         return ret ? GF_TRUE : GF_FALSE;
    1973             : }
    1974             : 
    1975             : 
    1976        4049 : Bool gf_sc_exec_event(GF_Compositor *compositor, GF_Event *evt)
    1977             : {
    1978             :         s32 x=0, y=0;
    1979             :         Bool switch_coords = GF_FALSE;
    1980             :         Bool ret = GF_FALSE;
    1981        4049 :         if (evt->type<=GF_EVENT_MOUSEWHEEL) {
    1982        3889 :                 if (compositor->sgaze) {
    1983           0 :                         compositor->gaze_x = evt->mouse.x;
    1984           0 :                         compositor->gaze_y = evt->mouse.y;
    1985             :                 }
    1986             : #ifndef GPAC_DISABLE_3D
    1987        3889 :                 else if ((compositor->visual->autostereo_type==GF_3D_STEREO_SIDE) || (compositor->visual->autostereo_type==GF_3D_STEREO_HEADSET)) {
    1988           0 :                         if (evt->mouse.x > compositor->visual->camera.proj_vp.width)
    1989           0 :                                 evt->mouse.x -= FIX2INT(compositor->visual->camera.proj_vp.width);
    1990           0 :                         evt->mouse.x *= 2;
    1991             :                 }
    1992        3889 :                 else if (compositor->visual->autostereo_type==GF_3D_STEREO_TOP) {
    1993           0 :                         if (evt->mouse.y > compositor->visual->camera.proj_vp.height)
    1994           0 :                                 evt->mouse.y -= FIX2INT(compositor->visual->camera.proj_vp.height);
    1995           0 :                         evt->mouse.y *= 2;
    1996             :                 }
    1997             : #endif
    1998             : 
    1999        3889 :                 if (compositor->visual->center_coords) {
    2000        3871 :                         x = evt->mouse.x;
    2001        3871 :                         y = evt->mouse.y;
    2002        3871 :                         evt->mouse.x = evt->mouse.x - compositor->display_width/2;
    2003        3871 :                         evt->mouse.y = compositor->display_height/2 - evt->mouse.y;
    2004             :                         switch_coords = GF_TRUE;
    2005             :                 }
    2006             :         }
    2007             : 
    2008             :         /*process regular events except if navigation is grabbed*/
    2009        4049 :         if ( (compositor->navigation_state<2) && (compositor->interaction_level & GF_INTERACT_NORMAL)) {
    2010             :                 Bool res;
    2011        3979 :                 res = gf_sc_execute_event(compositor, compositor->traverse_state, evt, NULL);
    2012        3979 :                 if (res) {
    2013        2164 :                         compositor->navigation_state = 0;
    2014             :                         ret = GF_TRUE;
    2015             :                 }
    2016             :         }
    2017        4049 :         if (switch_coords) {
    2018        3871 :                 evt->mouse.x = x;
    2019        3871 :                 evt->mouse.y = y;
    2020             : 
    2021             :         }
    2022             : 
    2023        4049 :         if (!ret) {
    2024             : #ifndef GPAC_DISABLE_3D
    2025             :                 /*remember active layer on mouse click - may be NULL*/
    2026        1885 :                 if ((evt->type==GF_EVENT_MOUSEDOWN) && (evt->mouse.button==GF_MOUSE_LEFT))
    2027          37 :                         compositor->active_layer = compositor->traverse_state->layer3d;
    2028             : #endif
    2029             : 
    2030        1885 :         ret = forward_event(compositor, evt, ret);
    2031             :     }
    2032             :     //if event is consumed before forwarding don't apply navigation
    2033             :     else {
    2034        2164 :         forward_event(compositor, evt, ret);
    2035             :     }
    2036             :         
    2037        4049 :         if (!ret) {
    2038             :                 /*process navigation events*/
    2039         112 :                 if (compositor->interaction_level & GF_INTERACT_NAVIGATION)
    2040         112 :                         ret = compositor_handle_navigation(compositor, evt);
    2041             :         }
    2042        4049 :         return ret;
    2043             : }
    2044             : 
    2045          21 : void gf_sc_change_key_navigator(GF_Compositor *sr, GF_Node *n)
    2046             : {
    2047             : #ifndef GPAC_DISABLE_VRML
    2048             :         GF_Node *par;
    2049             :         M_KeyNavigator *kn;
    2050             : 
    2051          21 :         gf_list_reset(sr->focus_ancestors);
    2052             : 
    2053          21 :         if (sr->keynav_node) {
    2054             :                 kn = (M_KeyNavigator*)sr->keynav_node;
    2055          20 :                 kn->focusSet = 0;
    2056          20 :                 gf_node_event_out(sr->keynav_node, 9/*"focusSet"*/);
    2057             :         }
    2058          21 :         sr->keynav_node = n;
    2059             :         kn = (M_KeyNavigator*)n;
    2060          21 :         if (n) {
    2061          21 :                 kn->focusSet = 1;
    2062          21 :                 gf_node_event_out(sr->keynav_node, 9/*"focusSet"*/);
    2063             :         }
    2064             : 
    2065          21 :         par = n ? kn->sensor : NULL;
    2066          21 :         if (par) par = gf_node_get_parent(par, 0);
    2067             : 
    2068          21 :         gf_sc_focus_switch_ring(sr, GF_FALSE, par, 1);
    2069             : #endif
    2070          21 : }
    2071             : 
    2072           4 : void gf_sc_key_navigator_del(GF_Compositor *sr, GF_Node *n)
    2073             : {
    2074           4 :         if (sr->keynav_node==n) {
    2075           1 :                 sr->keynav_node = NULL;
    2076             :         }
    2077           4 : }
    2078             : 
    2079             : 
    2080             : 

Generated by: LCOV version 1.13