d1cdc3781254619e637f9627d7bf8150c709df9a
[sdk] / extras / html / tables.ec
1 import "HTMLView"
2
3 class Column : struct
4 {
5    Column prev, next;
6    int w;         // Actual width for current rendering
7    int minW, lineW;
8    int rowSpan;
9    int width;
10    int desire;
11 };
12
13 void ComputeTable(Surface surface, Block table, int textPos, int * width, int * height, int maxW, int maxH, RenderFlags flags, int sy, int sx)
14 {
15    if(flags.minW || flags.lineW || flags.width)
16    {
17       Block row;
18       Column column;
19
20       int w = 0, h = 0;
21       int startX = sx;
22
23       // Pass 1: Figure out column widths
24
25       for(column = table.columns.first; column; column = column.next)
26       {
27          column.w = 0;
28
29          if(flags.minW)
30          {
31             column.minW = 0;
32             column.width = 0;
33          }
34          if(flags.lineW)
35          {
36             column.lineW = 0;
37          }
38          // Temporary variable:
39          column.rowSpan = 0;
40       }
41
42       for(row = table.subBlocks.first; row && row.type != TABLE; )
43       {
44          if(row.type == TR)
45          {
46             Block cell;
47             column = table.columns.first;
48             for(cell = row.subBlocks.first; cell; cell = cell.next)
49             {
50                if(cell.type == TD)
51                {
52                   int c;
53                   bool centered = false;
54                   int minW = 0, lineW = 0;
55                   Block block;
56                   int textPos = 0;
57                   // Disconnect the cell
58                   Block parent = cell.parent;
59                   Block next = cell.next;
60                   cell.parent = null;
61                   cell.next = null;
62
63                   while(column && column.rowSpan)
64                      column = column.next;
65
66                   if(!column)
67                   {
68                      column = Column { rowSpan = cell.rowSpan };
69                      // if(cell.rowSpan) Do proper thing if 0 rowSpan
70
71                      table.columns.Add(column);
72                   }
73
74                   // Process whole cell
75                   block = cell.subBlocks.first;
76                   for(;block;)
77                   {
78                      int w;
79                      Block nextCellBlock;
80                      int nextCellPos;
81
82                      // Minimum width for this column
83                      if(flags.minW)
84                      {
85                         if(cell.noWrap)
86                         {
87                            ComputeLine(surface, block, textPos, &nextCellBlock, &nextCellPos, &centered, &w, MAXINT, 0, RenderFlags { lineW = true }, 0, null, null, null, true, sy, sx);
88                            /*if(cell.pWidth)
89                               w = Max(maxW * cell.pWidth / 100, w);*/
90                         }
91                         else
92                            ComputeLine(surface, block, textPos, &nextCellBlock, &nextCellPos, &centered, &w, 0, 0, flags, 0, null, null, null, true, sy, sx);
93
94                         // Width specified absolute is minimum width
95                         /*
96                         if(cell.width)
97                            w = Max(cell.width, w);
98                         */
99
100                         minW = Max(minW, w);
101                      }
102                      if(flags.lineW)
103                      {
104                         // Width specified absolute will not extend
105                         if(maxW < cell.minW || cell.width)
106                         {
107                            //lineW = cell.minW;
108                            lineW = Max(cell.minW, cell.width);
109                            break;
110                         }
111                         else
112                         {
113                            ComputeLine(surface, block, textPos, &nextCellBlock, &nextCellPos, &centered, &w, maxW, 0, RenderFlags { lineW = true }, 0, null, null, null, true, sy, sx);
114                            if(cell.pWidth)
115                               w = Max(maxW * cell.pWidth / 100, w);
116                            lineW = Max(lineW, w);
117                         }
118                      }
119
120                      textPos = nextCellPos;
121                      block = nextCellBlock;
122                   }
123
124                   cell.parent = parent;
125                   cell.next = next;
126
127                   minW += table.cellPadding * 2;
128                   lineW += table.cellPadding * 2;
129
130                   if(flags.minW)
131                      cell.minW = minW;
132                   if(flags.lineW)
133                      cell.lineW = lineW;
134
135                   // First only process non spanning cells
136                   if(cell.span == 1)
137                   {
138                      if(flags.minW)
139                      {
140                         column.width = Max(column.width, cell.width);
141                         column.minW = Max(column.minW, cell.minW);
142                      }
143                      if(flags.lineW)
144                      {
145                         column.lineW = Max(column.lineW, cell.lineW);
146                         column.minW = Max(column.minW, cell.minW);                        
147                      }
148                   }
149
150                   for(c = 0; c<cell.span && column; c++)
151                      column = column.next;
152                }
153             }
154
155             for(column = table.columns.first; column; column = column.next)
156                if(column.rowSpan)
157                   column.rowSpan--;
158
159             row = NextBlockUp(surface, row, null, 0);
160          }
161          else
162             row = NextBlock(surface, row, null, 0);
163       }
164
165       // Start with bare minimum
166       table.w = 0;
167       w = 0;
168       for(column = table.columns.first; column; column = column.next)
169       {
170          column.w = column.minW;
171          w += column.w;
172       }
173       if(w > table.w)
174          table.w = w;
175
176       //if(flags.width)
177       {
178          if(flags.lineW /*&& flags.width*/)
179          {
180             // Expand using lineW
181             if(!table.pWidth && !table.width)
182             {
183                int totalLineW = 0;
184
185                for(column = table.columns.first; column; column = column.next)
186                   column.rowSpan = 0;
187                for(row = table.subBlocks.first; row && row.type != TABLE; )
188                {
189                   if(row.type == TR)
190                   {
191                      Block cell;
192                      int rowLineW = 0;
193
194                      column = table.columns.first;
195
196                      for(cell = row.subBlocks.first; cell; cell = cell.next)
197                      {
198                         int c;
199                         int sumColW = 0;
200
201                         while(column && column.rowSpan)
202                            column = column.next;
203
204                         for(c = 0; c<cell.span && column; c++)
205                         {
206                            column.rowSpan = cell.rowSpan;
207                            sumColW += column.w;
208                            column = column.next;  
209                         }
210                         rowLineW += Max(sumColW, cell.lineW);
211                      }
212                      totalLineW = Max(totalLineW, rowLineW);
213                      row = NextBlockUp(surface, row, null, 0);
214                   }
215                   else
216                      row = NextBlock(surface, row, null, 0);
217
218                   // MOVED THIS UP HERE
219                   for(column = table.columns.first; column; column = column.next)
220                      if(column.rowSpan) column.rowSpan--;
221                }
222
223                totalLineW = Min(maxW, totalLineW);
224                table.w = Max(table.w, totalLineW);
225                /*
226                for(column = table.columns.first; column; column = column.next)
227                   if(column.rowSpan) column.rowSpan--;
228                */
229             }
230          }
231
232          // Expand table more (only expand absolute if we're in that pass)
233          if(table.pWidth && (flags.width))
234          {
235             table.w = Max(table.w, maxW * table.pWidth / 100);
236          }
237          else if(table.width)
238          {
239             table.w = Max(table.w, table.width);
240          }
241
242          // Repartition the rest of the space in the columns according to column.lineW
243          if(w < table.w)
244          {
245             int needed = 0;
246             
247             // Step 1: Weights how to distribute
248             for(column = table.columns.first; column; column = column.next)
249             {
250                column.rowSpan = 0;
251                column.desire = 0;
252             }
253             for(row = table.subBlocks.first; row && row.type != TABLE; )
254             {
255                if(row.type == TR)
256                {
257                   Block cell;
258                   Column columnStart = table.columns.first;
259
260                   for(cell = row.subBlocks.first; cell; cell = cell.next)
261                   {
262                      int c;
263                      int totalW = 0;
264
265                      while(columnStart && columnStart.rowSpan)
266                         columnStart = columnStart.next;
267                   
268                      for(c = 0, column = columnStart; c<cell.span && column; c++)
269                      {
270                         column.rowSpan = cell.rowSpan;
271                         if(cell.span == 1)
272                            totalW += column.w;
273                         column = column.next;  
274                      }
275                      if(cell.span == 1 && totalW < cell.lineW)
276                      {
277                         for(c = 0, column = columnStart; c<cell.span && column; c++, column = column.next)
278                         {
279                            int desire = cell.lineW - totalW;
280                            column.desire += desire / cell.span;
281                         }
282                         needed += cell.lineW - totalW;
283                      }
284
285                      columnStart = column;  
286                   }
287                   for(column = table.columns.first; column; column = column.next)
288                      if(column.rowSpan) column.rowSpan--;
289                   row = NextBlockUp(surface, row, null, 0);
290                }
291                else
292                   row = NextBlock(surface, row, null, 0);
293             }
294
295             // Take care of spanning across columns (minw)
296             for(column = table.columns.first; column; column = column.next)
297                column.rowSpan = 0;
298
299             for(row = table.subBlocks.first; row && row.type != TABLE; )
300             {
301                if(row.type == TR)
302                {
303                   Block cell;
304                   Column columnStart = table.columns.first;
305
306                   for(cell = row.subBlocks.first; cell; cell = cell.next)
307                   {
308                      int c;
309                      int totalW = 0;
310                      int sumDesires = 0;
311
312                      while(columnStart && columnStart.rowSpan)
313                         columnStart = columnStart.next;
314
315                      for(c = 0, column = columnStart; c<cell.span && column; c++)
316                      {
317                         column.rowSpan = cell.rowSpan;
318                         totalW += column.w;
319                         sumDesires += column.desire;
320                         column = column.next;  
321                      }
322                      if(cell.span > 1 && totalW < cell.minW)
323                      {
324                         for(c = 0, column = columnStart; c<cell.span && column; c++)
325                         {
326                            if(column.desire)
327                            {
328                               int given = (column.desire) * (cell.minW - totalW) / sumDesires;
329                               column.minW += given;
330                            }
331                            column = column.next;
332                         }
333
334                         // Check if we've got enough now...
335                         totalW = 0;
336                         for(c = 0, column = columnStart; c<cell.span && column; c++)
337                         {
338                            totalW += column.minW;
339                            column = column.next;  
340                         }
341
342                         if(totalW < cell.minW)
343                         {
344                            for(c = 0, column = columnStart; c<cell.span && column; c++)
345                            {
346                               column.minW = Max(column.minW, (cell.minW - totalW) / cell.span);
347                               column = column.next;
348                            }
349                         }
350                      }
351                      columnStart = column;  
352                   }
353                   for(column = table.columns.first; column; column = column.next)
354                      if(column.rowSpan) column.rowSpan--;
355                   row = NextBlockUp(surface, row, null, 0);
356                }
357                else
358                   row = NextBlock(surface, row, null, 0);
359             }
360
361             for(column = table.columns.first; column; column = column.next)
362             {
363                if(column.minW > column.w)
364                {
365                   w += column.minW - column.w;
366                   column.w = column.minW;
367                }
368             }
369             
370             
371             // Repeat Step 1: Weights how to distribute
372             needed = 0;
373             for(column = table.columns.first; column; column = column.next)
374             {
375                column.rowSpan = 0;
376                column.desire = 0;
377             }
378             for(row = table.subBlocks.first; row && row.type != TABLE; )
379             {
380                if(row.type == TR)
381                {
382                   Block cell;
383                   Column columnStart = table.columns.first;
384
385                   for(cell = row.subBlocks.first; cell; cell = cell.next)
386                   {
387                      int c;
388                      int totalW = 0;
389
390                      while(columnStart && columnStart.rowSpan)
391                         columnStart = columnStart.next;
392                   
393                      for(c = 0, column = columnStart; c<cell.span && column; c++)
394                      {
395                         column.rowSpan = cell.rowSpan;
396                         if(/*cell.span == 1 && */cell.width)
397                            totalW += column.w;
398                         column = column.next;  
399                      }
400                      if(/*cell.span == 1 && */totalW < cell.lineW && cell.width)
401                      {
402                         for(c = 0, column = columnStart; c<cell.span && column; c++, column = column.next)
403                         {
404                            int desire = cell.lineW - totalW;
405                            column.desire += desire / cell.span;
406                         }
407                         needed += cell.lineW - totalW;
408                      }
409
410                      columnStart = column;  
411                   }
412                   for(column = table.columns.first; column; column = column.next)
413                      if(column.rowSpan) column.rowSpan--;
414                      row = NextBlockUp(surface, row, null, 0);
415                }
416                else
417                   row = NextBlock(surface, row, null, 0);
418             }
419                         
420             // Step 2: Do the distribution
421             if(needed)
422             {
423                for(column = table.columns.first; column; column = column.next)
424                {
425                   if(column.width)
426                   {
427                      int give = (column.desire) * (table.w - w) / needed;
428                      give = Min(give, column.width - column.w);
429                      if(give > 0)
430                         column.w += give;
431                   }
432                }
433             }
434             w = 0;
435             for(column = table.columns.first; column; column = column.next)
436                w += column.w;
437          }
438
439          if(w<table.w)
440          {
441             // Repease Step 1: Weights how to distribute
442             int needed = 0;
443             for(column = table.columns.first; column; column = column.next)
444             {
445                column.rowSpan = 0;
446                column.desire = 0;
447             }
448             for(row = table.subBlocks.first; row && row.type != TABLE; )
449             {
450                if(row.type == TR)
451                {
452                   Block cell;
453                   Column columnStart = table.columns.first;
454
455                   for(cell = row.subBlocks.first; cell; cell = cell.next)
456                   {
457                      int c;
458                      int totalW = 0;
459
460                      while(columnStart && columnStart.rowSpan)
461                         columnStart = columnStart.next;
462                   
463                      for(c = 0, column = columnStart; c<cell.span && column; c++)
464                      {
465                         column.rowSpan = cell.rowSpan;
466                         if(/*cell.span == 1 && */!cell.width)
467                            totalW += column.w;
468                         column = column.next;  
469                      }
470                      if(/*cell.span == 1 && */totalW < cell.lineW && !cell.width)
471                      {
472                         for(c = 0, column = columnStart; c<cell.span && column; c++, column = column.next)
473                         {
474                            int desire = cell.lineW - totalW;
475                            column.desire += desire / cell.span;
476                         }
477                         needed += cell.lineW - totalW;
478                      }
479
480                      columnStart = column;  
481                   }
482                   for(column = table.columns.first; column; column = column.next)
483                      if(column.rowSpan) column.rowSpan--;
484                   row = NextBlockUp(surface, row, null, 0);
485                }
486                else
487                   row = NextBlock(surface, row, null, 0);
488             }
489             
490             // Step 2: Do the distribution
491             if(needed)
492             {
493                for(column = table.columns.first; column; column = column.next)
494                {
495                   if(!column.width)
496                   {
497                      int give = (column.desire) * (table.w - w) / needed;
498                      column.w += give;
499                   }
500                }
501             }
502
503             w = 0;
504             for(column = table.columns.first; column; column = column.next)
505                w += column.w;
506          }
507          
508          // Repartition the rest of the space in the columns
509          if(w < table.w)
510          {
511             int numNotFixed = 0;
512             int totalLineW = 0;
513             for(column = table.columns.first; column; column = column.next)
514             {
515                if(!column.width)
516                {
517                   numNotFixed++;
518                   totalLineW += column.lineW;
519                }
520             }
521             if(numNotFixed)
522             {
523                for(column = table.columns.first; column; column = column.next)
524                {
525                   if(!column.width)
526                   {
527                      int give;
528                      if(totalLineW)
529                         give = (table.w - w) * column.lineW / totalLineW;
530                      else
531                         give = (table.w - w) / numNotFixed;
532                      column.w += give;
533                   }
534                }
535             }
536             else
537             {
538                for(column = table.columns.first; column; column = column.next)
539                   totalLineW += column.lineW;
540                for(column = table.columns.first; column; column = column.next)
541                {
542                   int give;
543                   if(totalLineW)
544                      give = (table.w - w) * column.lineW / totalLineW;
545                   else
546                      give = (table.w - w) / table.columns.count;
547                   column.w += give;
548                }
549             }
550             w = 0;
551             for(column = table.columns.first; column; column = column.next)
552                w += column.w;
553          }
554
555
556          {
557             /*switch(table.halign)
558             {
559                case middle:
560                   startX = Max(left, (left + right - table.w)/2);
561                   break;
562                case HorizontalAlignment::right:
563                   startX = Max(left, right - table.w);
564                   break;
565             }*/
566          }
567
568          // Pass 2: Figure out heights
569          h = 0;
570          for(column = table.columns.first; column; column = column.next)
571             column.rowSpan = 0;
572          for(row = table.subBlocks.first; row && row.type != TABLE; )
573          {
574             if(row.type == TR)
575             {
576                Block cell;
577                OldList leftObjects { };
578                OldList rightObjects { };
579
580                row.height = 0;
581
582                column = table.columns.first;
583                row.h = 0;
584
585                sx = startX;
586                for(cell = row.subBlocks.first; cell; cell = cell.next)
587                {
588                   if(cell.type == TD)
589                   {
590                      bool centered = false;
591                      Block block;
592                      int textPos = 0;
593                      int c;
594                      int cellMaxH = row.h;
595                      int lineH = 0;
596                      int y = 0;
597                      AlignedObject object, nextObject;
598
599                      // Disconnect the cell
600                      Block parent = cell.parent;
601                      Block next = cell.next;
602                      cell.parent = null;
603                      cell.next = null;
604
605                      if(column)
606                      {
607                         if(column.rowSpan)
608                            column = column.next;
609                         // TOCHECK: Added column null check here..
610                         if(column && cell.rowSpan)
611                            column.rowSpan = cell.rowSpan;
612                      }
613
614                      cell.w = 0;
615                      for(c = 0; (!cell.span || c<cell.span) && column; c++)
616                      {
617                         cell.w += column.w;
618                         column = column.next;
619                      }
620
621                      cell.h = 0;
622
623                      block = cell.subBlocks.first;
624                      for(;block;)
625                      {
626                         int newLineH, lineW;
627                         Block nextCellBlock;
628                         int nextCellPos;
629                         bool changeLine;
630                         int cellW = cell.w;
631
632                         for(object = leftObjects.last; object; object = nextObject)
633                         {
634                            nextObject = object.prev;
635                            if(y < object.untilY || object.next)
636                               cellW -= object.w;
637                            else
638                               leftObjects.Delete(object);
639                         }
640                         for(object = rightObjects.last; object; object = nextObject)
641                         {
642                            nextObject = object.prev;
643                            if(y < object.untilY || object.next)
644                               cellW -= object.w;
645                            else
646                               rightObjects.Delete(object);
647                         }
648                         cellW = Max(cellW, 0);
649  
650                         // TRIED ADDING THIS CODE HERE...
651                         {
652                            int x;
653                            int left, right;
654                            // Compute aligned objects
655                            left = sx + table.cellPadding; // Add cell border/margins here?
656                            right = sx + cellW - table.cellPadding; // Subtract cell border/margins here?
657
658                            for(object = leftObjects.last; object; object = nextObject)
659                            {
660                               nextObject = object.prev;
661                               if(y < object.untilY || object.next)
662                                  left += object.w;
663                               else
664                                  leftObjects.Delete(object);
665                            }
666                            for(object = rightObjects.last; object; object = nextObject)
667                            {
668                               nextObject = object.prev;
669                               if(y < object.untilY || object.next)
670                                  right -= object.w;
671                               else
672                                  rightObjects.Delete(object);
673                            }
674                            right = Max(left, right);
675
676                            if(cell.halign == middle /*|| thisLineCentered*/)
677                            {
678                               x = (left + right - lineW)/2;
679                               x = Max(x, left);
680                            }
681                            else if(cell.halign == HorizontalAlignment::right)
682                            {
683                               x = right - lineW;
684                               x = Max(x, left);
685                            }
686                            else
687                               x = left;
688
689                            newLineH = ComputeLine(surface, block, textPos, &nextCellBlock, &nextCellPos, &centered, &lineW, right - left /*cellW*/, cellMaxH, flags, y, &leftObjects, &rightObjects, &changeLine, true, sy, x);
690                         }
691                         textPos = nextCellPos;
692                         block = nextCellBlock;
693
694                         lineH = Max(lineH, newLineH);
695                         if(changeLine)
696                         {
697                            //cell.h += lineH;
698                            //cellMaxH -= lineH;
699
700                            cell.h += newLineH;
701                            y += newLineH;
702                            cellMaxH -= newLineH;
703                            //lineH = 0;
704                         }
705                      }
706
707                      for(object = leftObjects.last; object; object = nextObject)
708                      {
709                         nextObject = object.prev;
710                         y = Max(y, object.untilY);
711                         leftObjects.Delete(object);
712                      }
713                      for(object = rightObjects.last; object; object = nextObject)
714                      {
715                         nextObject = object.prev;
716                         y = Max(y, object.untilY);
717                         rightObjects.Delete(object);
718                      }
719                      sx += cell.w;
720                      cell.h = y;
721
722                      //cell.h += lineH;
723                      //cellMaxH -= lineH;
724
725                      cell.parent = parent;
726                      cell.next = next;
727
728                      row.height = Max(row.height, cell.height);
729
730                      cell.h = Max(cell.h, cell.height);
731
732                      row.h = Max(row.h, cell.h);
733                   }
734                }
735                // row.h = Max(row.h, row.height);
736                h += row.h;
737                sy += row.h;
738
739                for(column = table.columns.first; column; column = column.next)
740                {
741                   if(column.rowSpan)
742                      column.rowSpan--;
743                }
744                row = NextBlockUp(surface, row, null, 0);
745             }
746             else
747                row = NextBlock(surface, row, null, 0);
748          }
749
750          if(table.pHeight)
751             table.h = maxH * table.pHeight / 100;
752          else if(table.height)
753             table.h = table.height;
754          else
755             table.h = 0;
756
757          if(h > table.h)
758          {
759             table.h = h;
760          }
761
762          if(table.h > h)
763          {
764             int numNotFixed = 0;
765             // Repartition the rest of the space in the rows
766             for(row = table.subBlocks.first; row && row.type != TABLE; )
767             {
768                if(row.type == TR)
769                {
770                   if(!row.height)
771                      numNotFixed++;
772                   row = NextBlockUp(surface, row, null, 0);
773                }
774                else
775                   row = NextBlock(surface, row, null, 0);
776             }
777             if(numNotFixed)
778             {
779                for(row = table.subBlocks.first; row && row.type != TABLE; )
780                {
781                   if(row.type == TR)
782                   {
783                      row.h += (table.h - h) / numNotFixed;
784                      row = NextBlockUp(surface, row, null, 0);
785                   }
786                   else
787                      row = NextBlock(surface, row, null, 0);
788                }
789             }
790          }
791       }
792    }
793
794    *height = table.h;
795    *width = table.w;
796 }
797
798 static void RenderCell(HTMLView browser, Surface surface, Block cell, int cellX, int y)
799 {
800    bool centered = false;
801    Block block = cell;
802    int textPos = 0;
803    Block row = cell.parent;
804    int x;
805    int maxH = row.h;
806    int cellW = cell.w;
807    int lineH = 0;
808    AlignedObject object, nextObject;
809    OldList leftObjects { };
810    OldList rightObjects { };
811    Block table;
812
813    // Disconnect the cell
814    Block parent = cell.parent;
815    Block next = cell.next;
816    table = cell;
817    while(table && table.type != TABLE) table = table.parent;
818
819    cell.parent = null;
820    cell.next = null;
821    
822    /*
823    if(cell.width)
824       cellW = cell.width;
825    */
826
827    if(cell.bitmap)
828    {
829       ColorAlpha fg = surface.GetForeground();
830       surface.SetForeground(white);
831
832       // surface.Stretch(cell.bitmap, x,y,0,0,cell.w,cell.h,cell.bitmap.width, cell.bitmap.height);
833       surface.Tile(cell.bitmap, cellX,y,cellW,row.h);
834
835       surface.SetForeground(fg);
836    }
837    else if(cell.bgColor)
838    {
839       surface.SetBackground(cell.bgColor);
840       surface.Area(cellX, y, cellX+cellW-1, y+row.h-1);
841    }
842
843    //surface.SetForeground(Color { 85,85,255 });
844    //surface.Rectangle(cellX, y, cellX+cellW-1, y+row.h-1);
845
846    if(cell.valign == middle)
847    {
848       y += (row.h - cell.h) / 2;
849    }
850    else if(cell.valign == bottom)
851    {
852       y += row.h - cell.h;
853    }
854    browser.isSelected = false;
855
856    // Render whole cell
857    while(block && table)
858    {
859       int lineW, newLineH;
860       Block nextCellBlock;
861       int nextCellPos;
862       int thisLineCentered = centered;
863       int left, right;
864       int maxW;
865       bool changeLine;
866
867       void * font = surface.GetFont();
868
869       // Compute aligned objects
870       left = cellX + table.cellPadding; // Add cell border/margins here?
871       right = cellX + cellW - table.cellPadding; // Subtract cell border/margins here?
872
873       for(object = leftObjects.last; object; object = nextObject)
874       {
875          nextObject = object.prev;
876          if(y < object.untilY || object.next)
877             left += object.w;
878          else
879             leftObjects.Delete(object);
880       }
881       for(object = rightObjects.last; object; object = nextObject)
882       {
883          nextObject = object.prev;
884          if(y < object.untilY || object.next)
885             right -= object.w;
886          else
887             rightObjects.Delete(object);
888       }
889       right = Max(left, right);
890       maxW = right - left;
891
892       newLineH = ComputeLine(surface, block, textPos, &nextCellBlock, &nextCellPos, &centered, &lineW, maxW, maxH, RenderFlags {}, y, &leftObjects, &rightObjects, &changeLine, false, 0, 0);
893
894       surface.TextFont(font);
895
896       if(cell.halign == middle || thisLineCentered)
897       {
898          x = (left + right - lineW)/2;
899          x = Max(x, left);
900       }
901       else if(cell.halign == HorizontalAlignment::right)
902       {
903          x = right - lineW;
904          x = Max(x, left);
905       }
906       else
907          x = left;
908
909       lineH = Max(lineH, newLineH);
910
911       RenderLine(browser, surface, x, y, cellW, newLineH, block, textPos, nextCellBlock, nextCellPos, left, right);
912
913       if(changeLine)
914       {
915          //y += lineH;
916          //maxH -= lineH;
917          y += newLineH;
918          maxH -= newLineH;
919          //lineH = 0;
920       }
921       textPos = nextCellPos;
922       block = nextCellBlock;
923    }
924    //y += lineH;
925    //maxH -= lineH;
926    for(object = leftObjects.last; object; object = nextObject)
927    {
928       nextObject = object.prev;
929       y = Max(y, object.untilY);
930       leftObjects.Delete(object);
931    }
932    for(object = rightObjects.last; object; object = nextObject)
933    {
934       nextObject = object.prev;
935       y = Max(y, object.untilY);
936       rightObjects.Delete(object);
937    }
938
939    cell.parent = parent;
940    cell.next = next;
941 }
942
943
944 static void RenderRow(HTMLView browser, Surface surface, Block row, Block table, int x, int y)
945 {
946    Block cell;
947    int c = 0;
948    Column column = table.columns.first;
949    for(cell = row.subBlocks.first; cell; cell = cell.next)
950    {
951       if(cell.type == TD)
952       {
953          int c;
954
955          while(column && column.rowSpan)
956             column = column.next;
957
958          RenderCell(browser, surface, cell, x, y);
959
960          for(c = 0; c<cell.span && column; c++)
961          {
962             column.rowSpan = cell.rowSpan;
963             x += column.w;
964             column = column.next;  
965          }
966       }
967       c++;
968    }
969 }
970
971 void RenderTable(HTMLView browser, Surface surface, int x, int y, int w, int h, int left, int right, Block table)
972 {
973    Block row;
974    Column column;
975
976    switch(table.halign)
977    {
978       case middle:
979          x = Max(left, (left + right - table.w)/2);
980          break;
981       case HorizontalAlignment::right:
982          x = Max(left, right - table.w);
983          break;
984    }
985
986    if(table.bitmap)
987    {
988       ColorAlpha fg = surface.GetForeground();
989       surface.SetForeground(white);
990
991       // surface.Stretch(table.bitmap, x,y,0,0,table.w,table.h,table.bitmap.width, table.bitmap.height);
992       surface.Tile(table.bitmap, x,y,table.w,table.h);
993
994       surface.SetForeground(fg);
995    }
996    else if(table.bgColor)
997    {
998       surface.SetBackground(table.bgColor);
999       surface.Area(x,y,x+table.w-1,y+table.h-1);
1000    }
1001
1002    for(column = table.columns.first; column; column = column.next)
1003       column.rowSpan = 0;
1004
1005    for(row = table.subBlocks.first; row && row.type != TABLE; )
1006    {
1007       if(row.type == TR)
1008       {
1009          RenderRow(browser, surface, row, table, x, y);
1010          y += row.h;
1011          for(column = table.columns.first; column; column = column.next)
1012          {
1013             if(column.rowSpan)
1014                column.rowSpan--;
1015          }
1016          row = NextBlockUp(surface, row, null, 0);
1017       }
1018       else
1019          row = NextBlock(surface, row, null, 0);   
1020    }
1021 }
1022
1023
1024 static bool PickCell(HTMLView browser, Surface surface, Block cell, int cellX, int y,
1025                      int pickX, int pickY, Block* pickBlock, int * pickTextPos)
1026 {
1027    bool result = false;
1028    bool centered = false;
1029    Block block = cell;
1030    int textPos = 0;
1031    Block row = cell.parent;
1032    int x;
1033    int maxH = row.h;
1034    int cellW = cell.w;
1035    int lineH = 0;
1036    AlignedObject object, nextObject;
1037    OldList leftObjects { };
1038    OldList rightObjects { };
1039
1040    // Disconnect the cell
1041    Block parent = cell.parent;
1042    Block next = cell.next;
1043    Block table = cell;
1044
1045    while(table && table.type != TABLE) table = table.parent;
1046
1047    cell.parent = null;
1048    cell.next = null;
1049
1050    if(cell.valign == middle)
1051    {
1052       y += (row.h - cell.h) / 2;
1053    }
1054    else if(cell.valign == bottom)
1055    {
1056       y += row.h - cell.h;
1057    }
1058
1059    // Render whole cell
1060    for(;block && table && !result;)
1061    {
1062       int lineW, newLineH;
1063       Block nextCellBlock;
1064       int nextCellPos;
1065       int thisLineCentered = centered;
1066       int left, right;
1067       int maxW;
1068       bool changeLine;
1069
1070       void * font = surface.GetFont();
1071
1072       // Compute aligned objects
1073       left = cellX + table.cellPadding; // Add cell border/margins here?
1074       right = cellX + cellW - table.cellPadding; // Subtract cell border/margins here?
1075
1076       for(object = leftObjects.last; object; object = nextObject)
1077       {
1078          nextObject = object.prev;
1079          if(y < object.untilY || object.next)
1080             left += object.w;
1081          else
1082             leftObjects.Delete(object);
1083       }
1084       for(object = rightObjects.last; object; object = nextObject)
1085       {
1086          nextObject = object.prev;
1087          if(y < object.untilY || object.next)
1088             right -= object.w;
1089          else
1090             rightObjects.Delete(object);
1091       }
1092       right = Max(left, right);
1093       maxW = right - left;
1094
1095       newLineH = ComputeLine(surface, block, textPos, &nextCellBlock, &nextCellPos, &centered, &lineW, maxW, maxH, RenderFlags {}, y, &leftObjects, &rightObjects, &changeLine, false, 0, 0);
1096
1097       surface.TextFont(font);
1098
1099       if(cell.halign == middle || thisLineCentered)
1100       {
1101          x = (left + right - lineW)/2;
1102          x = Max(x, left);
1103       }
1104       else if(cell.halign == HorizontalAlignment::right)
1105       {
1106          x = right - lineW;
1107          x = Max(x, left);
1108       }
1109       else
1110          x = left;
1111
1112       lineH = Max(lineH, newLineH);
1113
1114       result = PickLine(browser, surface, x, y, cellW, newLineH, block, textPos, nextCellBlock, nextCellPos, left, right, pickX, pickY, pickBlock, pickTextPos);
1115
1116       if(changeLine)
1117       {
1118          //y += lineH;
1119          //maxH -= lineH;
1120          y += newLineH;
1121          maxH -= newLineH;
1122          //lineH = 0;
1123       }
1124       textPos = nextCellPos;
1125       block = nextCellBlock;
1126    }
1127    //y += lineH;
1128    //maxH -= lineH;
1129    for(object = leftObjects.last; object; object = nextObject)
1130    {
1131       nextObject = object.prev;
1132       y = Max(y, object.untilY);
1133       leftObjects.Delete(object);
1134    }
1135    for(object = rightObjects.last; object; object = nextObject)
1136    {
1137       nextObject = object.prev;
1138       y = Max(y, object.untilY);
1139       rightObjects.Delete(object);
1140    }
1141
1142    cell.parent = parent;
1143    cell.next = next;
1144    return result;
1145 }
1146
1147
1148 static bool PickRow(HTMLView browser, Surface surface, Block row, Block table, int x, int y,
1149                     int pickX, int pickY, Block* pickBlock, int * pickTextPos)
1150 {
1151    bool result = false;
1152    Block cell;
1153    int c = 0;
1154    Column column = table.columns.first;
1155    for(cell = row.subBlocks.first; cell; cell = cell.next)
1156    {
1157       if(cell.type == TD)
1158       {
1159          int c;
1160
1161          while(column && column.rowSpan)
1162             column = column.next;
1163
1164          result = PickCell(browser, surface, cell, x, y, pickX, pickY, pickBlock, pickTextPos);
1165          if(result)
1166             break;
1167
1168          for(c = 0; c<cell.span && column; c++)
1169          {
1170             column.rowSpan = cell.rowSpan;
1171             x += column.w;
1172             column = column.next;  
1173          }
1174       }
1175       c++;
1176    }
1177    return result;
1178 }
1179
1180 bool PickTable(HTMLView browser, Surface surface, int x, int y, int w, int h, int left, int right, Block table,
1181                int pickX, int pickY, Block* pickBlock, int * pickTextPos)
1182 {
1183    bool result = false;
1184    Block row;
1185    Column column;
1186
1187    switch(table.halign)
1188    {
1189       case middle:
1190          x = Max(left, (left + right - table.w)/2);
1191          break;
1192       case HorizontalAlignment::right:
1193          x = Max(left, right - table.w);
1194          break;
1195    }
1196
1197    for(column = table.columns.first; column; column = column.next)
1198       column.rowSpan = 0;
1199
1200    for(row = table.subBlocks.first; row && row.type != TABLE; )
1201    {
1202       if(row.type == TR)
1203       {
1204          result = PickRow(browser, surface, row, table, x, y, pickX, pickY, pickBlock, pickTextPos);
1205          y += row.h;
1206          for(column = table.columns.first; column; column = column.next)
1207          {
1208             if(column.rowSpan)
1209                column.rowSpan--;
1210          }
1211          if(result)
1212             break;
1213          row = NextBlockUp(surface, row, null, 0);
1214       }
1215       else
1216          row = NextBlock(surface, row, null, 0);   
1217    }
1218    return result;
1219 }
1220
1221 static void PositionCell(HTMLView browser, Surface surface, Block cell, int cellX, int y)
1222 {
1223    bool centered = false;
1224    Block block = cell;
1225    int textPos = 0;
1226    Block row = cell.parent;
1227    int x;
1228    int maxH = row.h;
1229    int cellW = cell.w;
1230    int lineH = 0;
1231    AlignedObject object, nextObject;
1232    OldList leftObjects { };
1233    OldList rightObjects { };
1234
1235    // Disconnect the cell
1236    Block parent = cell.parent;
1237    Block next = cell.next;
1238    Block table;
1239
1240    table = cell;
1241    while(table && table.type != TABLE) table = table.parent;
1242    cell.parent = null;
1243    cell.next = null;
1244
1245    if(cell.valign == middle)
1246    {
1247       y += (row.h - cell.h) / 2;
1248    }
1249    else if(cell.valign == bottom)
1250    {
1251       y += row.h - cell.h;
1252    }
1253
1254    // Render whole cell
1255    for(;block && table;)
1256    {
1257       int lineW, newLineH;
1258       Block nextCellBlock;
1259       int nextCellPos;
1260       int thisLineCentered = centered;
1261       int left, right;
1262       int maxW;
1263       bool changeLine;
1264
1265       void * font = surface.GetFont();
1266
1267       // Compute aligned objects
1268       left = cellX + table.cellPadding; // Add cell border/margins here?
1269       right = cellX + cellW - table.cellPadding; // Subtract cell border/margins here?
1270
1271       for(object = leftObjects.last; object; object = nextObject)
1272       {
1273          nextObject = object.prev;
1274          if(y < object.untilY || object.next)
1275             left += object.w;
1276          else
1277             leftObjects.Delete(object);
1278       }
1279       for(object = rightObjects.last; object; object = nextObject)
1280       {
1281          nextObject = object.prev;
1282          if(y < object.untilY || object.next)
1283             right -= object.w;
1284          else
1285             rightObjects.Delete(object);
1286       }
1287       right = Max(left, right);
1288       maxW = right - left;
1289
1290       newLineH = ComputeLine(surface, block, textPos, &nextCellBlock, &nextCellPos, &centered, &lineW, maxW, maxH, RenderFlags {}, y, &leftObjects, &rightObjects, &changeLine, false, 0, 0);
1291
1292       surface.TextFont(font);
1293
1294       if(cell.halign == middle || thisLineCentered)
1295       {
1296          x = (left + right - lineW)/2;
1297          x = Max(x, left);
1298       }
1299       else if(cell.halign == HorizontalAlignment::right)
1300       {
1301          x = right - lineW;
1302          x = Max(x, left);
1303       }
1304       else
1305          x = left;
1306
1307       lineH = Max(lineH, newLineH);
1308
1309       PositionLine(browser, surface, x, y, cellW, newLineH, block, textPos, nextCellBlock, nextCellPos, left, right);
1310
1311       if(changeLine)
1312       {
1313          //y += lineH;
1314          //maxH -= lineH;
1315          y += newLineH;
1316          maxH -= newLineH;
1317          //lineH = 0;
1318       }
1319       textPos = nextCellPos;
1320       block = nextCellBlock;
1321    }
1322    //y += lineH;
1323    //maxH -= lineH;
1324    for(object = leftObjects.last; object; object = nextObject)
1325    {
1326       nextObject = object.prev;
1327       y = Max(y, object.untilY);
1328       leftObjects.Delete(object);
1329    }
1330    for(object = rightObjects.last; object; object = nextObject)
1331    {
1332       nextObject = object.prev;
1333       y = Max(y, object.untilY);
1334       rightObjects.Delete(object);
1335    }
1336
1337    cell.parent = parent;
1338    cell.next = next;
1339 }
1340
1341
1342 static void PositionRow(HTMLView browser, Surface surface, Block row, Block table, int x, int y)
1343 {
1344    Block cell;
1345    int c = 0;
1346    Column column = table.columns.first;
1347    for(cell = row.subBlocks.first; cell; cell = cell.next)
1348    {
1349       if(cell.type == TD)
1350       {
1351          int c;
1352
1353          while(column && column.rowSpan)
1354             column = column.next;
1355
1356          PositionCell(browser, surface, cell, x, y);
1357
1358          for(c = 0; c<cell.span && column; c++)
1359          {
1360             column.rowSpan = cell.rowSpan;
1361             x += column.w;
1362             column = column.next;  
1363          }
1364       }
1365       c++;
1366    }
1367 }
1368
1369 void PositionTable(HTMLView browser, Surface surface, int x, int y, int w, int h, int left, int right, Block table)
1370 {
1371    Block row;
1372    Column column;
1373
1374    switch(table.halign)
1375    {
1376       case middle:
1377          x = Max(left, (left + right - table.w)/2);
1378          break;
1379       case HorizontalAlignment::right:
1380          x = Max(left, right - table.w);
1381          break;
1382    }
1383
1384    for(column = table.columns.first; column; column = column.next)
1385       column.rowSpan = 0;
1386
1387    for(row = table.subBlocks.first; row && row.type != TABLE; )
1388    {
1389       if(row.type == TR)
1390       {
1391          PositionRow(browser, surface, row, table, x, y);
1392          y += row.h;
1393          for(column = table.columns.first; column; column = column.next)
1394          {
1395             if(column.rowSpan)
1396                column.rowSpan--;
1397          }
1398          row = NextBlockUp(surface, row, null, 0);
1399       }
1400       else
1401          row = NextBlock(surface, row, null, 0);
1402    }
1403 }
1404