Coverage Report

Created: 2026-06-15 07:50

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