Coverage Report

Created: 2026-04-12 08:39

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