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 "nodes_stacks.h"
27 : #include "mpeg4_grouping.h"
28 : #include "visual_manager.h"
29 :
30 : #ifndef GPAC_DISABLE_VRML
31 :
32 : typedef struct
33 : {
34 : PARENT_MPEG4_STACK_2D
35 :
36 : Bool is_scrolling;
37 : u32 start_scroll_type;
38 : Double start_time, pause_time;
39 : GF_List *lines;
40 : GF_Rect clip;
41 : Fixed last_scroll, prev_rate, scroll_rate, scale_scroll, scroll_len, scroll_min, scroll_max;
42 :
43 : /*for keyboard navigation*/
44 : GF_SensorHandler hdl;
45 : s32 key_scroll;
46 : Bool keys_active;
47 : } LayoutStack;
48 :
49 : typedef struct
50 : {
51 : Fixed width, height, ascent, descent;
52 : u32 first_child, nb_children;
53 : Bool line_break;
54 : } LineInfo;
55 :
56 348 : static void layout_reset_lines(LayoutStack *st)
57 : {
58 612 : while (gf_list_count(st->lines)) {
59 264 : LineInfo *li = (LineInfo *)gf_list_get(st->lines, 0);
60 264 : gf_list_rem(st->lines, 0);
61 264 : gf_free(li);
62 : }
63 348 : }
64 :
65 264 : static LineInfo *new_line_info(LayoutStack *st)
66 : {
67 : LineInfo *li;
68 264 : GF_SAFEALLOC(li, LineInfo);
69 264 : if (li)
70 264 : gf_list_add(st->lines, li);
71 264 : return li;
72 : }
73 :
74 :
75 : enum
76 : {
77 : L_FIRST,
78 : L_BEGIN,
79 : L_MIDDLE,
80 : L_END,
81 : L_JUSTIFY,
82 : };
83 :
84 388 : static u32 get_justify(M_Layout *l, u32 i)
85 : {
86 388 : if (l->justify.count <= i) return L_BEGIN;
87 379 : if (!strcmp(l->justify.vals[i], "END")) return L_END;
88 283 : if (!strcmp(l->justify.vals[i], "MIDDLE")) return L_MIDDLE;
89 188 : if (!strcmp(l->justify.vals[i], "FIRST")) return L_FIRST;
90 164 : if (!strcmp(l->justify.vals[i], "SPREAD")) return L_JUSTIFY;
91 164 : if (!strcmp(l->justify.vals[i], "JUSTIFY")) return L_JUSTIFY;
92 : return L_BEGIN;
93 : }
94 :
95 178 : static void get_lines_info(LayoutStack *st, M_Layout *l)
96 : {
97 : Fixed prev_discard_width;
98 : u32 i, count;
99 : LineInfo *li;
100 : Fixed max_w, max_h;
101 :
102 178 : max_w = st->clip.width;
103 178 : max_h = st->clip.height;
104 178 : layout_reset_lines(st);
105 :
106 178 : count = gf_list_count(st->groups);
107 178 : if (!count) return;
108 :
109 172 : li = new_line_info(st);
110 172 : li->first_child = 0;
111 : prev_discard_width = 0;
112 :
113 797 : for (i=0; i<count; i++) {
114 797 : ChildGroup *cg = (ChildGroup *)gf_list_get(st->groups, i);
115 797 : if (!l->horizontal) {
116 : /*check if exceed column size or not - if so, move to next column or clip given wrap mode*/
117 298 : if (cg->final.height + li->height > max_h) {
118 40 : if (l->wrap) {
119 40 : li = new_line_info(st);
120 40 : li->first_child = i;
121 : }
122 : }
123 298 : if (cg->final.width > li->width) li->width = cg->final.width;
124 298 : li->height += cg->final.height;
125 298 : li->nb_children ++;
126 : } else {
127 499 : if ((cg->text_type==2) || (i && (cg->final.width + li->width> max_w))) {
128 52 : if (cg->text_type==2) li->line_break = 1;
129 52 : if (l->wrap) {
130 52 : if (!li->ascent) {
131 1 : li->ascent = li->height;
132 1 : li->descent = 0;
133 : }
134 : /*previous word is discardable (' ')*/
135 52 : if (prev_discard_width) {
136 3 : li->width -= prev_discard_width;
137 3 : li->nb_children--;
138 : }
139 52 : if ((cg->text_type==1) && (i+1==count)) break;
140 :
141 52 : li = new_line_info(st);
142 52 : li->first_child = i;
143 52 : if (cg->text_type) {
144 0 : li->first_child++;
145 0 : continue;
146 : }
147 : }
148 : }
149 :
150 : /*get ascent/descent for text or height for non-text*/
151 499 : if (cg->ascent) {
152 233 : if (li->ascent < cg->ascent) li->ascent = cg->ascent;
153 233 : if (li->descent < cg->descent) li->descent = cg->descent;
154 233 : if (li->height < li->ascent + li->descent) li->height = li->ascent + li->descent;
155 266 : } else if (cg->final.height > li->height) {
156 158 : li->height = cg->final.height;
157 : }
158 499 : li->width += cg->final.width;
159 499 : li->nb_children ++;
160 :
161 499 : prev_discard_width = (cg->text_type==1) ? cg->final.width : 0;
162 :
163 : }
164 : }
165 : }
166 :
167 :
168 178 : static void layout_justify(LayoutStack *st, M_Layout *l)
169 : {
170 : u32 first, minor, major, i, k, nbLines;
171 : Fixed current_top, current_left, h;
172 : LineInfo *li;
173 : ChildGroup *cg, *prev;
174 178 : get_lines_info(st, l);
175 178 : major = get_justify(l, 0);
176 178 : minor = get_justify(l, 1);
177 :
178 178 : st->scroll_len = 0;
179 178 : nbLines = gf_list_count(st->lines);
180 178 : if (l->horizontal) {
181 116 : if (l->wrap && !l->topToBottom) {
182 24 : li = (LineInfo*)gf_list_get(st->lines, 0);
183 24 : current_top = st->clip.y - st->clip.height;
184 24 : if (li) current_top += li->height;
185 : } else {
186 92 : current_top = st->clip.y;
187 : }
188 :
189 : /*for each line perform adjustment*/
190 162 : for (k=0; k<nbLines; k++) {
191 : Fixed spacing = 0;
192 162 : li = (LineInfo*)gf_list_get(st->lines, k);
193 162 : first = li->first_child;
194 162 : if (!l->leftToRight) first += li->nb_children - 1;
195 :
196 162 : if (!l->topToBottom && k) current_top += li->height;
197 :
198 : /*set major alignment (X) */
199 162 : cg = (ChildGroup *)gf_list_get(st->groups, first);
200 162 : if (!cg) continue;
201 162 : switch (major) {
202 49 : case L_END:
203 49 : cg->final.x = st->clip.x + st->clip.width - li->width;
204 49 : break;
205 49 : case L_MIDDLE:
206 49 : cg->final.x = st->clip.x + (st->clip.width - li->width)/2;
207 49 : break;
208 64 : case L_FIRST:
209 : case L_BEGIN:
210 64 : cg->final.x = st->clip.x;
211 64 : break;
212 0 : case L_JUSTIFY:
213 0 : cg->final.x = st->clip.x;
214 0 : if (li->nb_children>1) {
215 0 : cg = (ChildGroup *)gf_list_get(st->groups, li->nb_children-1);
216 0 : spacing = (st->clip.width - li->width) / (li->nb_children-1) ;
217 0 : if (spacing<0) spacing = 0;
218 0 : else if (cg->ascent) {
219 : /*disable spacing for last text line and line breaks*/
220 0 : if ( (k+1==nbLines) || li->line_break) {
221 : spacing = 0;
222 : }
223 : }
224 : }
225 : break;
226 : }
227 :
228 :
229 : /*for each in the run */
230 : i = first;
231 : while (1) {
232 496 : cg = (ChildGroup *)gf_list_get(st->groups, i);
233 496 : if (!cg) break;
234 496 : h = MAX(li->ascent, li->height);
235 496 : switch (minor) {
236 108 : case L_FIRST:
237 108 : cg->final.y = current_top - h;
238 108 : if (cg->ascent) {
239 48 : cg->final.y += cg->ascent;
240 : } else {
241 60 : cg->final.y += cg->final.height;
242 : }
243 : break;
244 108 : case L_MIDDLE:
245 108 : cg->final.y = current_top - (h - cg->final.height)/2;
246 108 : break;
247 134 : case L_END:
248 134 : cg->final.y = current_top;
249 134 : break;
250 146 : case L_BEGIN:
251 : default:
252 146 : cg->final.y = current_top - h + cg->final.height;
253 146 : break;
254 : }
255 : /*update left for non-first children in line*/
256 496 : if (i != first) {
257 334 : if (l->leftToRight) {
258 214 : prev = (ChildGroup *)gf_list_get(st->groups, i-1);
259 : } else {
260 120 : prev = (ChildGroup *)gf_list_get(st->groups, i+1);
261 : }
262 334 : cg->final.x = prev->final.x + prev->final.width + spacing;
263 : }
264 496 : i += l->leftToRight ? +1 : -1;
265 496 : if (l->leftToRight && (i==li->first_child + li->nb_children))
266 : break;
267 394 : else if (!l->leftToRight && (i==li->first_child - 1))
268 : break;
269 : }
270 162 : if (l->topToBottom) {
271 114 : current_top -= gf_mulfix(l->spacing, li->height);
272 : } else {
273 48 : current_top += gf_mulfix(l->spacing - FIX_ONE, li->height);
274 : }
275 162 : if (l->scrollVertical) {
276 156 : st->scroll_len += li->height;
277 : } else {
278 6 : if (st->scroll_len < li->width) st->scroll_len = li->width;
279 : }
280 : }
281 : return;
282 : }
283 :
284 : /*Vertical aligment*/
285 62 : li = (LineInfo*)gf_list_get(st->lines, 0);
286 62 : if (l->wrap && !l->leftToRight) {
287 18 : current_left = st->clip.x + st->clip.width;
288 18 : if (li) current_left -= li->width;
289 : } else {
290 44 : current_left = st->clip.x;
291 : }
292 :
293 : /*for all columns in run*/
294 102 : for (k=0; k<nbLines; k++) {
295 : Fixed spacing = 0;
296 102 : li = (LineInfo*)gf_list_get(st->lines, k);
297 :
298 102 : first = li->first_child;
299 102 : if (!l->topToBottom) first += li->nb_children - 1;
300 :
301 : /*set major alignment (Y) */
302 102 : cg = (ChildGroup *)gf_list_get(st->groups, first);
303 102 : switch (major) {
304 30 : case L_END:
305 30 : cg->final.y = st->clip.y - st->clip.height + li->height;
306 30 : break;
307 30 : case L_MIDDLE:
308 30 : cg->final.y = st->clip.y - st->clip.height/2 + li->height/2;
309 30 : break;
310 42 : case L_FIRST:
311 : case L_BEGIN:
312 42 : cg->final.y = st->clip.y;
313 42 : break;
314 0 : case L_JUSTIFY:
315 0 : cg->final.y = st->clip.y;
316 0 : if (li->nb_children>1) {
317 0 : spacing = (st->clip.height - li->height) / (li->nb_children-1) ;
318 0 : if (spacing<0) spacing = 0;
319 : }
320 : break;
321 : }
322 :
323 : /*for each in the run */
324 : i = first;
325 : while (1) {
326 298 : cg = (ChildGroup *)gf_list_get(st->groups, i);
327 298 : switch (minor) {
328 90 : case L_MIDDLE:
329 90 : cg->final.x = current_left + li->width/2 - cg->final.width/2;
330 90 : break;
331 90 : case L_END:
332 90 : cg->final.x = current_left + li->width - cg->final.width;
333 90 : break;
334 118 : case L_BEGIN:
335 : case L_FIRST:
336 : default:
337 118 : cg->final.x = current_left;
338 118 : break;
339 : }
340 : /*update top for non-first children in line*/
341 298 : if (i != first) {
342 196 : if (l->topToBottom) {
343 106 : prev = (ChildGroup *)gf_list_get(st->groups, i-1);
344 : } else {
345 90 : prev = (ChildGroup *)gf_list_get(st->groups, i+1);
346 : }
347 196 : cg->final.y = prev->final.y - prev->final.height + spacing;
348 : }
349 298 : i += l->topToBottom ? +1 : -1;
350 298 : if (l->topToBottom && (i==li->first_child + li->nb_children))
351 : break;
352 241 : else if (!l->topToBottom && (i==li->first_child - 1))
353 : break;
354 : }
355 102 : if (l->leftToRight) {
356 66 : current_left += gf_mulfix(l->spacing, li->width);
357 36 : } else if (k < nbLines - 1) {
358 18 : li = (LineInfo*)gf_list_get(st->lines, k+1);
359 18 : current_left -= gf_mulfix(l->spacing, li->width);
360 : }
361 102 : if (l->scrollVertical) {
362 96 : if (st->scroll_len < li->height) st->scroll_len = li->height;
363 : } else {
364 6 : st->scroll_len += li->width;
365 : }
366 : }
367 : }
368 :
369 25 : static void layout_setup_scroll_bounds(LayoutStack *st, M_Layout *l)
370 : {
371 : u32 minor_justify = 0;
372 :
373 25 : st->scroll_min = st->scroll_max = 0;
374 :
375 25 : if (l->horizontal) minor_justify = l->scrollVertical ? 1 : 0;
376 11 : else minor_justify = l->scrollVertical ? 0 : 1;
377 :
378 : /*update scroll-out max limit*/
379 25 : if (l->scrollMode != -1) {
380 : /*set max limit*/
381 13 : switch( get_justify(l, minor_justify)) {
382 0 : case L_END:
383 0 : if (l->scrollVertical) {
384 0 : if (st->scale_scroll<0) st->scroll_max = - st->scroll_len;
385 0 : else st->scroll_max = st->clip.height;
386 : } else {
387 0 : if (st->scale_scroll<0) st->scroll_max = - st->clip.width;
388 0 : else st->scroll_max = st->scroll_len;
389 : }
390 : break;
391 0 : case L_MIDDLE:
392 0 : if (l->scrollVertical) {
393 0 : if (st->scale_scroll<0) st->scroll_max = - (st->clip.height + st->scroll_len)/2;
394 0 : else st->scroll_max = (st->clip.height + st->scroll_len)/2;
395 : } else {
396 0 : if (st->scale_scroll<0) st->scroll_max = - (st->clip.width + st->scroll_len)/2;
397 0 : else st->scroll_max = (st->clip.width + st->scroll_len)/2;
398 : }
399 : break;
400 13 : default:
401 13 : if (l->scrollVertical) {
402 8 : if (st->scale_scroll<0) st->scroll_max = - st->clip.height;
403 4 : else st->scroll_max = st->scroll_len;
404 : } else {
405 5 : if (st->scale_scroll<0) st->scroll_max = - st->scroll_len;
406 3 : else st->scroll_max = st->clip.width;
407 : }
408 : break;
409 : }
410 : }
411 : /*scroll-in only*/
412 : else {
413 : st->scroll_max = 0;
414 : }
415 :
416 : /*scroll-out only*/
417 25 : if (l->scrollMode==1) {
418 : st->scroll_min = 0;
419 : return;
420 : }
421 :
422 : /*when vertically scrolling an horizontal layout, don't use vertical justification, only justify top/bottom lines*/
423 25 : if (l->horizontal && l->scrollVertical) {
424 6 : if (st->scale_scroll<0) {
425 3 : st->scroll_min = st->scroll_len;
426 : } else {
427 3 : st->scroll_min = - st->clip.height;
428 : }
429 : return;
430 : }
431 :
432 : /*update scroll-in offset*/
433 19 : switch( get_justify(l, minor_justify)) {
434 2 : case L_END:
435 2 : if (l->scrollVertical) {
436 0 : if (st->scale_scroll<0) st->scroll_min = st->clip.height;
437 0 : else st->scroll_min = - st->scroll_len;
438 : } else {
439 2 : if (st->scale_scroll<0) st->scroll_min = st->scroll_len;
440 2 : else st->scroll_min = -st->clip.width;
441 : }
442 : break;
443 2 : case L_MIDDLE:
444 2 : if (l->scrollVertical) {
445 0 : if (st->scale_scroll<0) st->scroll_min = (st->clip.height + st->scroll_len)/2;
446 0 : else st->scroll_min = - (st->clip.height + st->scroll_len)/2;
447 : } else {
448 2 : if (st->scale_scroll<0) st->scroll_min = (st->clip.width + st->scroll_len)/2;
449 2 : else st->scroll_min = - (st->clip.width + st->scroll_len)/2;
450 : }
451 : break;
452 15 : default:
453 15 : if (l->scrollVertical) {
454 6 : if (st->scale_scroll<0) st->scroll_min = st->scroll_len;
455 5 : else st->scroll_min = - st->clip.height;
456 : } else {
457 9 : if (st->scale_scroll<0) st->scroll_min = st->clip.width;
458 7 : else st->scroll_min = - st->scroll_len;
459 : }
460 : break;
461 : }
462 : }
463 :
464 :
465 879 : static void layout_scroll(GF_TraverseState *tr_state, LayoutStack *st, M_Layout *l)
466 : {
467 : u32 i, nb_lines;
468 : Fixed scrolled, rate, elapsed, scroll_diff;
469 : Bool smooth, do_scroll, stop_anim;
470 : Double time = 0;
471 : ChildGroup *cg;
472 :
473 : /*not scrolling*/
474 1757 : if (!st->scale_scroll && !st->is_scrolling && !st->key_scroll) return;
475 :
476 433 : if (st->key_scroll) {
477 0 : if (!st->is_scrolling) {
478 0 : layout_setup_scroll_bounds(st, l);
479 0 : st->is_scrolling = 1;
480 : }
481 :
482 0 : scrolled = st->last_scroll + INT2FIX(st->key_scroll);
483 : } else {
484 :
485 433 : time = gf_node_get_scene_time((GF_Node *)l);
486 :
487 : // if (st->scale_scroll && (st->prev_rate!=st->scale_scroll)) st->start_scroll_type = 1;
488 :
489 : /*if scroll rate changed to previous non-zero value, this is a
490 : scroll restart, don't re-update bounds*/
491 433 : if ((st->start_scroll_type==2) && (st->prev_rate==st->scale_scroll)) st->start_scroll_type = 0;
492 :
493 433 : if (st->start_scroll_type) {
494 19 : st->start_time = time;
495 19 : st->is_scrolling = 1;
496 19 : st->prev_rate = st->scale_scroll;
497 :
498 : /*continuous restart: use last scroll to update the start time. We must recompute scroll bounds
499 : since switching from scroll_rate >0 to <0 changes the bounds !*/
500 19 : if ((st->start_scroll_type==2) && st->scale_scroll) {
501 2 : Fixed cur_pos = st->scroll_min + st->last_scroll;
502 2 : layout_setup_scroll_bounds(st, l);
503 2 : cur_pos -= st->scroll_min;
504 2 : st->start_time = time - FIX2FLT(gf_divfix(cur_pos, st->scale_scroll));
505 : } else {
506 17 : layout_setup_scroll_bounds(st, l);
507 : }
508 19 : st->last_scroll = 0;
509 19 : st->start_scroll_type = 0;
510 : }
511 :
512 : /*handle pause/resume*/
513 433 : rate = st->scale_scroll;
514 433 : if (!rate) {
515 41 : if (!st->pause_time) {
516 1 : st->pause_time = time;
517 : } else {
518 : time = st->pause_time;
519 : }
520 41 : rate = st->prev_rate;
521 392 : } else if (st->pause_time) {
522 1 : st->start_time += (time - st->pause_time);
523 1 : st->pause_time = 0;
524 : }
525 :
526 : /*compute advance in pixels for smooth scroll*/
527 433 : elapsed = FLT2FIX((Float) (time - st->start_time));
528 433 : scrolled = gf_mulfix(elapsed, rate);
529 : }
530 :
531 433 : smooth = l->smoothScroll;
532 : /*if the scroll is in the same direction as the layout, there is no notion of line or column to scroll
533 : so move to smooth mode*/
534 433 : if (!l->horizontal && l->scrollVertical) smooth = 1;
535 366 : else if (l->horizontal && !l->scrollVertical) smooth = 1;
536 :
537 :
538 : stop_anim = 0;
539 : /*compute scroll diff for non-smooth mode*/
540 200 : if (smooth) {
541 : do_scroll = 1;
542 : } else {
543 : Fixed dim;
544 0 : scroll_diff = scrolled - st->last_scroll;
545 : do_scroll = 0;
546 :
547 0 : nb_lines = gf_list_count(st->lines);
548 0 : for (i=0; i < nb_lines; i++) {
549 0 : LineInfo *li = (LineInfo*)gf_list_get(st->lines, i);
550 0 : dim = l->scrollVertical ? li->height : li->width;
551 0 : if (st->key_scroll) {
552 0 : if (st->key_scroll<0) dim = -dim;
553 0 : scrolled = dim + st->last_scroll;
554 : /*in key mode we must handle the min ourselves since we can go below the scroll limit*/
555 0 : if (st->scroll_min > st->scroll_len + scrolled) {
556 0 : scrolled = st->scroll_min - st->scroll_len;
557 : } else {
558 : do_scroll = 1;
559 : }
560 : break;
561 0 : } else if (ABS(scroll_diff) >= dim) {
562 : // if (scroll_diff<0) scroll_diff = -dim;
563 : // else scroll_diff = dim;
564 : do_scroll = 1;
565 : break;
566 : }
567 : }
568 : }
569 :
570 433 : scroll_diff = st->scroll_max - st->scroll_min;
571 433 : if ((scroll_diff<0) && (scrolled<=scroll_diff)) {
572 : stop_anim = 1;
573 : scrolled = scroll_diff;
574 : }
575 433 : else if ((scroll_diff>0) && (scrolled>=scroll_diff)) {
576 : stop_anim = 1;
577 : scrolled = scroll_diff;
578 : }
579 :
580 433 : if (do_scroll)
581 433 : st->last_scroll = scrolled;
582 : else
583 0 : scrolled = st->last_scroll;
584 :
585 433 : i=0;
586 1347 : while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) {
587 914 : if (l->scrollVertical) {
588 376 : cg->scroll_y = st->scroll_min + scrolled;
589 376 : cg->scroll_x = 0;
590 : } else {
591 538 : cg->scroll_x = st->scroll_min + scrolled;
592 538 : cg->scroll_y = 0;
593 : }
594 : }
595 :
596 433 : if (st->key_scroll) {
597 0 : st->key_scroll = 0;
598 : return;
599 : }
600 : /*draw next frame*/
601 433 : if (!stop_anim) {
602 432 : tr_state->visual->compositor->force_next_frame_redraw = GF_TRUE;
603 : return;
604 : }
605 :
606 : /*done*/
607 1 : if (!l->loop) return;
608 :
609 : /*restart*/
610 1 : st->start_time = time;
611 1 : gf_sc_invalidate(tr_state->visual->compositor, NULL);
612 : }
613 :
614 :
615 1156 : static void TraverseLayout(GF_Node *node, void *rs, Bool is_destroy)
616 : {
617 : Bool recompute_layout;
618 : u32 i;
619 : ChildGroup *cg;
620 : GF_IRect prev_clip;
621 1156 : Bool mode_bckup, had_clip=GF_FALSE;
622 : ParentNode2D *parent_bck;
623 : GF_Rect prev_clipper;
624 : M_Layout *l = (M_Layout *)node;
625 1156 : LayoutStack *st = (LayoutStack *) gf_node_get_private(node);
626 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
627 :
628 1156 : if (is_destroy) {
629 170 : layout_reset_lines(st);
630 170 : parent_node_predestroy((ParentNode2D *)st);
631 170 : gf_list_del(st->lines);
632 170 : gf_free(st);
633 170 : return;
634 : }
635 :
636 : /*note we don't clear dirty flag, this is done in traversing*/
637 986 : if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) {
638 :
639 : /*TO CHANGE IN BIFS - scroll_rate is quite unusable*/
640 179 : st->scale_scroll = st->scroll_rate = l->scrollRate;
641 : /*move to pixel metrics*/
642 179 : if (visual_get_size_info(tr_state, &st->clip.width, &st->clip.height)) {
643 179 : st->scale_scroll = gf_mulfix(st->scale_scroll, l->scrollVertical ? st->clip.height : st->clip.width);
644 : }
645 : /*setup bounds in local coord system*/
646 179 : if (l->size.x>=0) st->clip.width = l->size.x;
647 179 : if (l->size.y>=0) st->clip.height = l->size.y;
648 179 : st->bounds = st->clip = gf_rect_center(st->clip.width, st->clip.height);
649 :
650 179 : if (st->scale_scroll && !st->start_scroll_type) st->start_scroll_type = 1;
651 :
652 179 : drawable_reset_group_highlight(tr_state, node);
653 : }
654 :
655 : /*don't waste time traversing is pick ray not in clipper*/
656 986 : if ((tr_state->traversing_mode==TRAVERSE_PICK) && !gf_sc_pick_in_clipper(tr_state, &st->clip))
657 : goto layout_exit;
658 :
659 880 : if ((tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) && !tr_state->for_node) {
660 1 : tr_state->bounds = st->clip;
661 : #ifndef GPAC_DISABLE_3D
662 1 : gf_bbox_from_rect(&tr_state->bbox, &st->clip);
663 : #endif
664 1 : goto layout_exit;
665 : }
666 :
667 : recompute_layout = 0;
668 879 : if (gf_node_dirty_get(node))
669 : recompute_layout = 1;
670 :
671 : /*setup clipping*/
672 879 : prev_clip = tr_state->visual->top_clipper;
673 879 : if (tr_state->traversing_mode==TRAVERSE_SORT) {
674 871 : compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 0);
675 871 : if (tr_state->has_clip) {
676 871 : tr_state->visual->top_clipper = gf_rect_pixelize(&tr_state->clipper);
677 871 : gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip);
678 : }
679 : }
680 879 : if (recompute_layout) {
681 178 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layout] recomputing positions\n"));
682 :
683 178 : parent_node_reset((ParentNode2D*)st);
684 :
685 : /*setup traversing state*/
686 178 : parent_bck = tr_state->parent;
687 178 : mode_bckup = tr_state->traversing_mode;
688 178 : tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
689 178 : tr_state->parent = (ParentNode2D *) st;
690 :
691 178 : if (l->wrap) tr_state->text_split_mode = 1;
692 178 : parent_node_traverse(node, (ParentNode2D *)st, tr_state);
693 : /*restore traversing state*/
694 178 : tr_state->parent = parent_bck;
695 178 : tr_state->traversing_mode = mode_bckup;
696 178 : if (l->wrap) tr_state->text_split_mode = 0;
697 :
698 : /*center all nodes*/
699 178 : i=0;
700 1153 : while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) {
701 797 : cg->final.x = - cg->final.width/2;
702 797 : cg->final.y = cg->final.height/2;
703 : }
704 :
705 : /*apply justification*/
706 178 : layout_justify(st, l);
707 :
708 : /*if scrolling, update bounds*/
709 178 : if (l->scrollRate && st->is_scrolling) {
710 6 : layout_setup_scroll_bounds(st, l);
711 : }
712 : }
713 :
714 :
715 879 : if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
716 0 : tr_state->bounds = st->bounds;
717 0 : if (l->scrollVertical) {
718 0 : tr_state->bounds.height = st->scroll_len;
719 : } else {
720 0 : tr_state->bounds.width = st->scroll_len;
721 : }
722 : #ifndef GPAC_DISABLE_3D
723 0 : gf_bbox_from_rect(&tr_state->bbox, &tr_state->bounds);
724 : #endif
725 0 : goto layout_exit;
726 : }
727 :
728 :
729 : /*scroll*/
730 879 : layout_scroll(tr_state, st, l);
731 :
732 879 : i=0;
733 4138 : while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) {
734 2380 : parent_node_child_traverse(cg, tr_state);
735 : }
736 879 : tr_state->visual->top_clipper = prev_clip;
737 879 : if (tr_state->traversing_mode==TRAVERSE_SORT) {
738 871 : if (had_clip) tr_state->clipper = prev_clipper;
739 871 : tr_state->has_clip = had_clip;
740 :
741 871 : drawable_check_focus_highlight(node, tr_state, &st->clip);
742 : }
743 :
744 1100 : layout_exit:
745 986 : tr_state->text_split_mode = 0;
746 : }
747 :
748 0 : static Bool OnLayout(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
749 : {
750 : Bool vertical;
751 : LayoutStack *st;
752 0 : if (!sh || !ev) return GF_FALSE;
753 :
754 0 : st = (LayoutStack *) gf_node_get_private(sh->sensor);
755 0 : vertical = ((M_Layout *)sh->sensor)->scrollVertical;
756 :
757 0 : if (!is_over) {
758 0 : st->is_scrolling = 0;
759 0 : st->key_scroll = 0;
760 0 : return 0;
761 : }
762 0 : if (ev->type!=GF_EVENT_KEYDOWN) {
763 0 : st->is_scrolling = 0;
764 0 : st->key_scroll = 0;
765 0 : return 0;
766 : }
767 :
768 0 : switch (ev->key.key_code) {
769 0 : case GF_KEY_LEFT:
770 0 : if (!st->keys_active) return 0;
771 :
772 0 : if (vertical) return 0;
773 0 : st->key_scroll = -1;
774 0 : break;
775 0 : case GF_KEY_RIGHT:
776 0 : if (!st->keys_active) return 0;
777 :
778 0 : if (vertical) return 0;
779 0 : st->key_scroll = +1;
780 0 : break;
781 0 : case GF_KEY_UP:
782 0 : if (!st->keys_active) return 0;
783 :
784 0 : if (!vertical) return 0;
785 0 : st->key_scroll = +1;
786 0 : break;
787 0 : case GF_KEY_DOWN:
788 0 : if (!st->keys_active) return 0;
789 :
790 0 : if (!vertical) return 0;
791 0 : st->key_scroll = -1;
792 0 : break;
793 0 : case GF_KEY_ENTER:
794 0 : st->keys_active = !st->keys_active;
795 0 : break;
796 0 : default:
797 0 : st->key_scroll = 0;
798 0 : return 0;
799 : }
800 0 : gf_sc_invalidate(compositor, NULL);
801 0 : return 1;
802 : }
803 :
804 0 : static Bool layout_is_enabled(GF_Node *node)
805 : {
806 : M_Layout *l = (M_Layout *)node;
807 0 : if (node && (l->scrollRate != 0)) return 0;
808 0 : return 1;
809 : }
810 :
811 170 : void compositor_init_layout(GF_Compositor *compositor, GF_Node *node)
812 : {
813 : LayoutStack *stack;
814 170 : GF_SAFEALLOC(stack, LayoutStack);
815 170 : if (!stack) {
816 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate layout stack\n"));
817 : return;
818 : }
819 :
820 170 : parent_node_setup((ParentNode2D*)stack);
821 170 : stack->lines = gf_list_new();
822 170 : gf_node_set_private(node, stack);
823 170 : gf_node_set_callback_function(node, TraverseLayout);
824 170 : stack->hdl.sensor = node;
825 170 : stack->hdl.IsEnabled = layout_is_enabled;
826 170 : stack->hdl.OnUserEvent = OnLayout;
827 : #ifdef GPAC_ENABLE_COVERAGE
828 170 : if (gf_sys_is_cov_mode()) {
829 : OnLayout(NULL, GF_FALSE, GF_FALSE, NULL, compositor);
830 : layout_is_enabled(node);
831 : compositor_mpeg4_layout_get_sensor_handler(node);
832 : }
833 : #endif
834 :
835 : }
836 :
837 8 : void compositor_layout_modified(GF_Compositor *compositor, GF_Node *node)
838 : {
839 8 : LayoutStack *st = (LayoutStack *) gf_node_get_private(node);
840 : /*if modif other than scrollrate restart scroll*/
841 8 : if (st->scroll_rate == ((M_Layout*)node)->scrollRate) {
842 5 : st->start_scroll_type = 1;
843 : }
844 : /*if modif on scroll rate only, indicate continous restart*/
845 3 : else if (((M_Layout*)node)->scrollRate) {
846 2 : st->start_scroll_type = 2;
847 : }
848 8 : gf_node_dirty_set(node, GF_SG_NODE_DIRTY, 0);
849 8 : gf_sc_invalidate(compositor, NULL);
850 8 : }
851 :
852 0 : GF_SensorHandler *compositor_mpeg4_layout_get_sensor_handler(GF_Node *node)
853 : {
854 : LayoutStack *st;
855 170 : if (!node) return NULL;
856 170 : st = (LayoutStack *) gf_node_get_private(node);
857 0 : return &st->hdl;
858 : }
859 :
860 :
861 : #endif /*GPAC_DISABLE_VRML*/
|