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