documentor; extras/html: Made it easier to position caret at beginning or end of...
[sdk] / extras / html / lines.ec
1 import "HTMLView"
2 import "tables"
3
4 Block GetNextBlock(Block block)
5 {
6    // Do we have children?
7    if(block.subBlocks.first)
8       block = block.subBlocks.first;
9    else
10    {
11       for(;block;)
12       {
13          // Do we have younger siblings?
14          if(block.next)
15          {
16             block = block.next;
17             break;
18          }
19          block = block.parent;
20       }
21    }
22    return block;
23 }
24
25 /*static */Block NextBlockUp(Surface surface, Block block, int * centered, RenderFlags flags)
26 {
27    for(;block;)
28    {
29       // Do we have younger siblings?
30       if(block.next)
31       {
32          if(block.type == FONT || block.type == ANCHOR)
33          {
34             surface.TextFont(block.parent.font.font);
35             if(flags.render)
36                surface.SetForeground(block.parent.textColor);
37          }
38          block = block.next;
39          break;
40       }
41
42       //if(block)
43       {
44          if(block.type == FONT || block.type == ANCHOR)
45          {
46             surface.TextFont(block.parent.font.font);
47             if(flags.render)
48                surface.SetForeground(block.parent.textColor);
49          }
50       }
51       block = block.parent;
52
53       // Getting out of a block
54       if(block)
55       {
56          /*if(block.type == FONT || block.type == ANCHOR)
57          {
58             surface.TextFont(block.prevFont.font.font);
59             if(flags.render)
60                surface.SetForeground(block.prevFont.textColor);
61          }
62          else */if(block.type == CENTER)
63          {
64             if(centered)
65                (*centered)--;
66          }
67       }
68    }
69    return block;
70 }
71
72 /*static */Block NextBlock(Surface surface, Block block, int * centered, uint flags)
73 {
74    // Do we have children?
75    if(block.subBlocks.first)
76       block = block.subBlocks.first;
77    else
78    {
79       block = NextBlockUp(surface, block, centered, flags);
80    }
81    return block;
82 }
83
84 int ComputeLine(Surface surface, Block startBlock, int startTextPos, Block * nextBlock, int * nextTextPos, int * centered, int * w,
85    int maxW, int maxH, uint flags, int y, OldList leftObjects, OldList rightObjects, bool * changeLine, bool computeStartY, int sy, int sx)
86 {
87    int h = 0;
88    int x = 0;
89    bool lineComplete = false;
90    AlignedObject object;
91
92    Block block = startBlock;
93    int textPos = startTextPos;
94    int width = 0, height = 0;
95
96    int centeredBefore = *centered;
97
98    if(changeLine)
99       *changeLine = true;
100
101    *nextBlock = block;
102    *nextTextPos = textPos;
103
104    for(;!lineComplete && block;)
105    {
106       if(computeStartY && startTextPos == 0)
107       {
108          block.startX = x + sx;
109          block.startY = y + sy;
110       }
111       switch(block.type)
112       {
113          case INPUT:
114          {
115             if(block.window)
116             {
117                width += block.window.size.w;
118                height = Max(height, Max(26, block.window.size.h));
119             }
120             break;
121          }
122          case IMAGE:
123          {
124             int bw = block.pWidth ? (maxW * block.pWidth / 100) : block.w;
125             int bh = block.pHeight ? (maxH * block.pHeight / 100) : block.h;
126
127             if(block.halign == left || block.halign == right)
128             {
129                height = Max(height, bh);
130                h = Max(h, height);
131                if(changeLine)
132                   *changeLine = false;
133                if(leftObjects && rightObjects)
134                {
135                   object = AlignedObject { w = bw, untilY = y + bh };
136                   if(block.halign == left)
137                      leftObjects.Add(object);
138                   else
139                      rightObjects.Add(object);
140                }
141                lineComplete = true;
142                *nextBlock = NextBlockUp(surface, block, centered, flags);
143                *nextTextPos = 0;
144             }
145             else
146             {
147                *nextBlock = block;
148                *nextTextPos = 0;
149                h = Max(h, height);
150                x += width;
151                width = 0;
152                if(x + width + bw > maxW && x > 0)
153                {
154                   lineComplete = true;
155                }
156                else
157                {
158                   width += bw;
159                   height = Max(height, bh);
160                }
161             }
162             break;
163          }
164          case TEXT:
165          {
166             char * text = block.text;
167             int th;
168
169             // TO FIX: THIS USED TO BE COMMENTED... PUT IT BACK FOR DOCUMENTOR
170             surface.TextExtent(" ", 1, null, &th);
171             height = Max(height, th);
172
173             for(; textPos<block.textLen && !lineComplete;)
174             {
175                int w;
176                int len;
177                char * nextSpace = strchr(text + textPos, ' ');
178
179                if(nextSpace)
180                   len = (nextSpace - (text + textPos)) + 1;
181                else
182                   len = block.textLen - textPos;
183
184                surface.TextExtent(text + textPos, len, &w, &th);
185
186                if(x + width + w > maxW && x > 0)
187                {
188                   lineComplete = true;
189                   break;
190                }
191
192                textPos += len;
193
194                width += w;
195                height = Max(height, th);
196
197                if(nextSpace)
198                {
199                   *nextTextPos = textPos;
200                   *nextBlock = block;
201                   h = Max(h, height);
202                   x += width;
203                   width = 0;
204                }
205             }
206             block.height += height;
207             break;
208          }
209          case FONT:
210          case ANCHOR:
211             surface.TextFont(block.font.font);
212             break;
213          case BR:
214          {
215             int th;
216             surface.TextExtent(" ", 1, null, &th);
217
218             lineComplete = true;
219             *nextBlock = NextBlock(surface, block, null, flags);
220             *nextTextPos = 0;
221             height = Max(height, th);
222             h = Max(h, height);
223             x += width;
224             width = 0;
225             break;
226          }
227          case CENTER:
228             (*centered)++;
229             break;
230          case TABLE:
231             if(width || x)
232             {
233                lineComplete = true;
234                *nextBlock = block;
235                *nextTextPos = 0;
236                h = Max(h, height);
237                x += width;
238                width = 0;
239             }
240             else if(block.halign == left || block.halign == right)
241             {
242                Font font = surface.font;
243                ComputeTable(surface, block, textPos, &width, &height, maxW, maxH, flags, y + sy, x + sx);
244                surface.font = font;
245                x += width;
246
247                *nextBlock = NextBlockUp(surface, block, centered, flags);
248                *nextTextPos = 0;
249                h = Max(h, height);
250                if(changeLine)
251                   *changeLine = false;
252
253                if(leftObjects && rightObjects)
254                {
255                   object = AlignedObject { w = block.w, untilY = y + block.h };
256                   if(block.halign == left)
257                      leftObjects.Add(object);
258                   else
259                      rightObjects.Add(object);
260                }
261                lineComplete = true;
262             }
263             else
264             {
265                Font font = surface.font;
266                ComputeTable(surface, block, textPos, &width, &height, maxW, maxH, flags, y + sy, x + sx);
267                surface.font = font;
268                lineComplete = true;
269                *nextBlock = NextBlockUp(surface, block, centered, flags);
270                *nextTextPos = 0;
271                h = Max(h, height);
272                x += width;
273                width = 0;
274             }
275             break;
276       }
277
278       if(lineComplete || !block)
279          break;
280
281       if(textPos >= block.textLen)
282       {
283          textPos = 0;
284          block = NextBlock(surface, block, centered, flags);
285          if(block && block.type == TEXT)
286             block.height = 0;
287          // Break line after </center>
288          if(centeredBefore != *centered)
289          {
290             lineComplete = true;
291             *nextBlock = block;
292             *nextTextPos = 0;
293             h = Max(h, height);
294             x += width;
295             width = 0;
296          }
297       }
298    }
299    if(!lineComplete)
300    {
301       *nextBlock = null;
302       *nextTextPos = 0;
303       h = Max(h, height);
304       x += width;
305       width = 0;
306    }
307    *w = x;
308    if(x > 1000000)
309    {
310       printf("bug");
311    }
312    return h;
313 }
314
315 void RenderLine(HTMLView browser, Surface surface, int x, int y, int w, int h, Block startBlock, int startTextPos, Block endBlock, int endTextPos, int left, int right)
316 {
317    int textPos = startTextPos;
318    Block block = startBlock;
319    int startSel, endSel;
320    Block startSelBlock = null, endSelBlock = null;
321    int prevGlyph = 0;
322    if(browser.textBlock != browser.selBlock || browser.curPosition != browser.selPosition)
323       browser.NormalizeSelection(&startSelBlock, &startSel, &endSelBlock, &endSel);
324
325    for(;;)
326    {
327       Color fore = surface.foreground, back = surface.background;
328       if(block == endBlock && textPos >= endTextPos)
329          break;
330
331       switch(block.type)
332       {
333          case INPUT:
334          {
335             if(block.window)
336             {
337                x += block.window.size.w;
338             }
339             break;
340          }
341          case BODY:
342             surface.SetForeground(block.textColor);
343             break;
344          case IMAGE:
345          {
346             int bw = block.pWidth ? (w * block.pWidth / 100) : block.w;
347             int bh = block.pHeight ? (h * block.pHeight / 100) : block.h;
348
349             int dx, dy;
350
351             ColorAlpha fg = surface.GetForeground();
352             surface.SetForeground(white);
353
354             switch(block.halign)
355             {
356                case HorizontalAlignment::left:
357                   block.valign = top;
358                   dx = x;
359                   break;
360                case HorizontalAlignment::right:
361                   block.valign = top;
362                   dx = x + w - bw;
363                   dx = Max(x, dx);
364                   break;
365                case middle:
366                   dx = x;
367                   break;
368             }
369
370             switch(block.valign)
371             {
372                case bottom: dy = y + h - bh; break;
373                case top: dy = y; break;
374                case middle: dy = y + (h - bh) / 2; break;
375             }
376
377             if(block.bitmap)
378             {
379                if(bw == block.bitmap.width && bh == block.bitmap.height)
380                   surface.Blit(block.bitmap, dx,dy,0,0,bw,bh);
381                else
382                   surface.Stretch(block.bitmap, dx,dy,0,0,bw,bh,block.bitmap.width, block.bitmap.height);
383             }
384             else if(block.imageEntry && block.imageEntry.missing)
385             {
386                surface.Bevel(false, dx, dy, bw, bh);
387                if(browser.missing.bitmap)
388                   surface.Blit(browser.missing.bitmap, dx + 5, dy + 5, 0,0,
389                      browser.missing.bitmap.width, browser.missing.bitmap.height);
390             }
391             surface.SetForeground(fg);
392             x += bw;
393             break;
394          }
395          case TEXT:
396          {
397             int tw, th;
398             int endPos = (block == endBlock) ? endTextPos : block.textLen;
399             int len = endPos - textPos;
400
401             if(startSelBlock && block == startSelBlock && startSel >= textPos && startSel <= textPos + len)
402             {
403                int l = startSel - textPos;
404                if(block.text)
405                {
406                   surface.TextExtent2(block.text + textPos, l, &tw, &th, prevGlyph, &prevGlyph, null);
407                   surface.WriteText(x, y + h - th, block.text + textPos, l);
408                   x += tw;
409                }
410                textPos += l;
411                browser.isSelected = true;
412                len -= l;
413             }
414
415             if(endSelBlock && block == endSelBlock && endPos > textPos && endSel >= textPos && endSel < textPos + len)
416                len = endSel - textPos;
417
418             if(block.text)
419             {
420                if(browser.isSelected)
421                {
422                   surface.background = Color { 10, 36, 106 };
423                   surface.foreground = white;
424                   surface.textOpacity = true;
425                }
426                surface.TextExtent2(block.text + textPos, len, &tw, &th, prevGlyph, &prevGlyph, null);
427                surface.WriteText(x, y + h - th, block.text + textPos, len);
428                x += tw;
429                if(browser.isSelected)
430                {
431                   surface.background = back;
432                   surface.foreground = fore;
433                   surface.textOpacity = false;
434                }
435             }
436             textPos += len;
437             if(block == endSelBlock && textPos >= endSel)
438                browser.isSelected = false;
439
440             if(endPos > textPos)
441             {
442                int l = endPos - textPos;
443                if(block.text)
444                {
445                   surface.TextExtent2(block.text + textPos, l, &tw, &th, prevGlyph, &prevGlyph, null);
446                   surface.WriteText(x, y + h - th, block.text + textPos, l);
447                   x += tw;
448                }
449                textPos += l;
450             }
451             break;
452          }
453          case FONT:
454          case ANCHOR:
455             surface.TextFont(block.font.font);
456             surface.SetForeground(block.textColor);
457             break;
458         case TABLE:
459             RenderTable(browser, surface, x, y, w, h, left, right, block);
460             block = NextBlockUp(surface, block, null, RenderFlags { render = true });
461             textPos = 0;
462             break;
463       }
464
465       if(block == endBlock && textPos >= endTextPos)
466          break;
467
468       if(textPos >= block.textLen)
469       {
470          block = NextBlock(surface, block, null, RenderFlags { render = true });
471          textPos = 0;
472       }
473    }
474 }
475
476 bool PickLine(HTMLView browser, Surface surface, int x, int y, int w, int h, Block startBlock, int startTextPos,
477               Block endBlock, int endTextPos, int left, int right, int pickX, int pickY, Block* pickBlock, int * pickTextPos)
478 {
479    bool result = false;
480    int textPos = startTextPos;
481    Block block = startBlock;
482
483    for(;!result;)
484    {
485       if(block == endBlock && textPos >= endTextPos)
486          break;
487
488       switch(block.type)
489       {
490          case INPUT:
491          {
492             if(block.window)
493             {
494                x += block.window.size.w;
495             }
496             break;
497          }
498          case IMAGE:
499          {
500             int bw = block.pWidth ? (w * block.pWidth / 100) : block.w;
501             int bh = block.pHeight ? (h * block.pHeight / 100) : block.h;
502             int dx, dy;
503
504             switch(block.halign)
505             {
506                case HorizontalAlignment::left:
507                   block.valign = top;
508                   dx = x;
509                   break;
510                case HorizontalAlignment::right:
511                   block.valign = top;
512                   dx = x + w - bw;
513                   dx = Max(x, dx);
514                   break;
515                case middle:
516                   dx = x;
517                   break;
518             }
519
520             switch(block.valign)
521             {
522                case bottom: dy = y + h - bh; break;
523                case top: dy = y; break;
524                case middle: dy = y + (h - bh) / 2; break;
525             }
526
527             if(block.bitmap || block.src)
528             {
529                if(pickX >= dx && pickY >= dy && pickX < dx + bw && pickY < dy + bh)
530                {
531                   *pickBlock = block;
532                   *pickTextPos = 0;
533                   result = true;
534                }
535             }
536             x += bw;
537             break;
538          }
539          case TEXT:
540          {
541             int len, tw, th;
542             if(block == endBlock)
543                len = endTextPos - textPos;
544             else
545                len = block.textLen - textPos;
546             surface.TextExtent(block.text + textPos, len, &tw, &th);
547             // eSurface_WriteText(surface, x, y + h - th, block.text + textPos, len);
548
549             if(block.text[0] == ' ' && block.text[1] == 0) pickX += tw; else
550             if(pickX >= x && pickY >= y+h-th && pickX < x + tw + 2 && pickY < y+h)
551             {
552                result = true;
553                *pickBlock = block;
554                // Have to properly compute this
555                *pickTextPos = 0;
556             }
557             textPos += len;
558             x += tw;
559             break;
560          }
561          case FONT:
562          case ANCHOR:
563             surface.TextFont(block.font.font);
564             break;
565         case TABLE:
566             result = PickTable(browser, surface, x, y, w, h, left, right, block, pickX, pickY, pickBlock, pickTextPos);
567             block = NextBlockUp(surface, block, null, RenderFlags { render = true });
568             textPos = 0;
569             break;
570       }
571
572       if(block == endBlock && textPos >= endTextPos)
573          break;
574
575       if(textPos >= block.textLen)
576       {
577          block = NextBlock(surface, block, null, RenderFlags { render = true });
578          textPos = 0;
579       }
580    }
581    return result;
582 }
583
584
585 void PositionLine(HTMLView browser, Surface surface, int x, int y, int w, int h, Block startBlock, int startTextPos,
586                   Block endBlock, int endTextPos, int left, int right)
587 {
588    int textPos = startTextPos;
589    Block block = startBlock;
590
591    for(;;)
592    {
593       if(block == endBlock && textPos >= endTextPos)
594          break;
595
596       switch(block.type)
597       {
598          case INPUT:
599          {
600             if(block.window)
601             {
602                Window parent = block.window.parent;
603                block.window.Move(
604                   x + parent.scroll.x, y + parent.scroll.y,
605                   block.window.size.w, block.window.size.h);
606                //block.window.visible = false;
607                x += block.window.size.w;
608             }
609             break;
610          }
611          case IMAGE:
612          {
613             int bw = block.pWidth ? (w * block.pWidth / 100) : block.w;
614             //int bh = block.pHeight ? (h * block.pHeight / 100) : block.h;
615             int dx;//, dy;
616
617             switch(block.halign)
618             {
619                case HorizontalAlignment::left:
620                   block.valign = top;
621                   dx = x;
622                   break;
623                case HorizontalAlignment::right:
624                   block.valign = top;
625                   dx = x + w - bw;
626                   dx = Max(x, dx);
627                   break;
628                case middle:
629                   dx = x;
630                   break;
631             }
632
633             /*
634             switch(block.valign)
635             {
636                case bottom: dy = y + h - bh; break;
637                case top: dy = y; break;
638                case middle: dy = y + (h - bh) / 2; break;
639             }
640             */
641
642             x += bw;
643             break;
644          }
645          case TEXT:
646          {
647             int len, tw, th;
648             if(block == endBlock)
649                len = endTextPos - textPos;
650             else
651                len = block.textLen - textPos;
652             surface.TextExtent(block.text + textPos, len, &tw, &th);
653             // eSurface_WriteText(surface, x, y + h - th, block.text + textPos, len);
654
655             textPos += len;
656             x += tw;
657             break;
658          }
659          case FONT:
660          case ANCHOR:
661             surface.TextFont(block.font.font);
662             break;
663         case TABLE:
664             PositionTable(browser, surface, x, y, w, h, left, right, block);
665             block = NextBlockUp(surface, block, null, RenderFlags { render = true });
666             textPos = 0;
667             break;
668       }
669
670       if(block == endBlock && textPos >= endTextPos)
671          break;
672
673       if(textPos >= block.textLen)
674       {
675          block = NextBlock(surface, block, null, RenderFlags { render = true });
676          textPos = 0;
677       }
678    }
679 }