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 :
|