KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > lobobrowser > html > renderer > TableMatrix


1 /*
2 GNU LESSER GENERAL PUBLIC LICENSE
3     Copyright (C) 2006 The Lobo Project
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19     Contact info: xamjadmin@users.sourceforge.net
20 */

21 package org.lobobrowser.html.renderer;
22 import java.awt.Color JavaDoc;
23 import java.awt.Dimension JavaDoc;
24 import java.awt.Graphics JavaDoc;
25 import java.awt.Insets JavaDoc;
26 import java.awt.Rectangle JavaDoc;
27 import java.awt.event.MouseEvent JavaDoc;
28 import java.util.*;
29
30 import org.lobobrowser.html.*;
31 import org.lobobrowser.html.domimpl.*;
32 import org.lobobrowser.html.style.*;
33 import org.w3c.dom.Node JavaDoc;
34 import org.w3c.dom.html2.HTMLTableCellElement;
35 import org.w3c.dom.html2.HTMLTableRowElement;
36
37 class TableMatrix {
38     //private static final NodeFilter ROWS_FILTER = new RowsFilter();
39
private static final NodeFilter COLUMNS_FILTER = new ColumnsFilter();
40     private final ArrayList ROWS = new ArrayList();
41     private final ArrayList ALL_CELLS = new ArrayList();
42     private final ArrayList ROW_ELEMENTS = new ArrayList();
43     private final HTMLElementImpl tableElement;
44     private final UserAgentContext parserContext;
45     private final HtmlRendererContext rendererContext;
46     private final FrameContext frameContext;
47     private final RElement relement;
48     private final RenderableContainer container;
49     
50     private SizeInfo[] columnSizes;
51     private SizeInfo[] rowSizes;
52     private int tableWidth;
53     private int tableHeight;
54     private int hasOldStyleBorder;
55
56     /**
57      * @param element
58      */

59     public TableMatrix(HTMLElementImpl element, UserAgentContext pcontext, HtmlRendererContext rcontext, FrameContext frameContext, RenderableContainer tableAsContainer, RElement relement) {
60         this.tableElement = element;
61         this.parserContext = pcontext;
62         this.rendererContext = rcontext;
63         this.frameContext = frameContext;
64         this.relement = relement;
65         this.container = tableAsContainer;
66     }
67     
68     public void finalize() throws Throwable JavaDoc {
69         super.finalize();
70     }
71     
72     public int getNumRows() {
73         return this.ROWS.size();
74     }
75     
76     public int getNumColumns() {
77         return this.columnSizes.length;
78     }
79     /**
80      * @return Returns the tableHeight.
81      */

82     public int getTableHeight() {
83         return this.tableHeight;
84     }
85     /**
86      * @return Returns the tableWidth.
87      */

88     public int getTableWidth() {
89         return this.tableWidth;
90     }
91     
92     //private int border;
93
private int cellSpacingY;
94     private int cellSpacingX;
95     private int widthsOfExtras;
96     private int heightsOfExtras;
97     private HtmlLength tableWidthLength;
98         
99     /**
100      * Called on every relayout.
101      * Element children might have changed.
102      */

103     public void reset(Insets JavaDoc insets, int availWidth, int availHeight) {
104         //TODO: Incorporate into build() and calculate
105
//sizes properly based on parameters.
106
ROWS.clear();
107         ALL_CELLS.clear();
108         ROW_ELEMENTS.clear();
109         String JavaDoc borderText = this.tableElement.getAttribute("border");
110         int border = 0;
111         if(borderText != null) {
112             try {
113                 border = Integer.parseInt(borderText);
114                 if(border < 0) {
115                     border = 0;
116                 }
117             } catch(NumberFormatException JavaDoc nfe) {
118                 // ignore
119
}
120         }
121         String JavaDoc cellSpacingText = this.tableElement.getAttribute("cellspacing");
122         int cellSpacing = 1;
123         if(cellSpacingText != null) {
124             try {
125                 //TODO: cellSpacing can be a percentage as well
126
cellSpacing = Integer.parseInt(cellSpacingText);
127                 if(cellSpacing < 0) {
128                     cellSpacing = 0;
129                 }
130             } catch(NumberFormatException JavaDoc nfe) {
131                 // ignore
132
}
133         }
134         this.cellSpacingX = cellSpacing;
135         this.cellSpacingY = cellSpacing;
136
137         this.tableWidthLength = TableMatrix.getWidthLength(this.tableElement, availWidth);
138
139         this.populateRows();
140         this.adjustForCellSpans();
141         this.createSizeArrays();
142         
143         // Calculate widths of extras
144
SizeInfo[] columnSizes = this.columnSizes;
145         int numCols = columnSizes.length;
146         int widthsOfExtras = insets.left + insets.right + (numCols + 1) * cellSpacing;
147         if(border > 0) {
148             widthsOfExtras += (numCols * 2);
149         }
150         this.widthsOfExtras = widthsOfExtras;
151         
152         // Calculate heights of extras
153
SizeInfo[] rowSizes = this.rowSizes;
154         int numRows = rowSizes.length;
155         int heightsOfExtras = insets.top + insets.bottom + (numRows + 1) * cellSpacing;
156         if(border > 0) {
157             heightsOfExtras += (numRows * 2);
158         }
159         this.heightsOfExtras = heightsOfExtras;
160         this.hasOldStyleBorder = border > 0 ? 1 : 0;
161     }
162     
163     public void build(int availWidth, int availHeight) {
164         int hasBorder = this.hasOldStyleBorder;
165         this.determineColumnSizes(hasBorder, this.cellSpacingX, this.cellSpacingY, availWidth);
166         this.determineRowSizes(hasBorder, this.cellSpacingY, availHeight);
167     }
168
169     private final HTMLTableRowElementImpl getParentRow(HTMLTableCellElementImpl cellNode) {
170         org.w3c.dom.Node JavaDoc parentNode = cellNode.getParentNode();
171         for(;;) {
172             if(parentNode instanceof HTMLTableRowElementImpl) {
173                 return (HTMLTableRowElementImpl) parentNode;
174             }
175             if(parentNode instanceof HTMLTableElementImpl) {
176                 return null;
177             }
178             parentNode = parentNode.getParentNode();
179         }
180     }
181     
182     private static HtmlLength getWidthLength(HTMLElementImpl element, int availWidth) {
183         try {
184             CSS2PropertiesImpl props = element.getCurrentStyle();
185             String JavaDoc widthText = props == null ? null : props.getWidth();
186             if(widthText == null) {
187                 return new HtmlLength(element.getAttribute("width"));
188             }
189             else {
190                 return new HtmlLength(HtmlValues.getPixelSize(widthText, element.getRenderState(), 0, availWidth));
191             }
192         } catch(Exception JavaDoc err) {
193             return null;
194         }
195     }
196
197     private static HtmlLength getHeightLength(HTMLElementImpl element, int availHeight) {
198         try {
199             CSS2PropertiesImpl props = element.getCurrentStyle();
200             String JavaDoc heightText = props == null ? null : props.getHeight();
201             if(heightText == null) {
202                 return new HtmlLength(element.getAttribute("height"));
203             }
204             else {
205                 return new HtmlLength(HtmlValues.getPixelSize(heightText, element.getRenderState(), 0, availHeight));
206             }
207         } catch(Exception JavaDoc err) {
208             return null;
209         }
210     }
211
212     /**
213      * Populates the ROWS and ALL_CELLS collections.
214      */

215     private void populateRows() {
216         HTMLElementImpl te = this.tableElement;
217         String JavaDoc cellPaddingText = te.getAttribute("cellpadding");
218         int cellPadding = 1;
219         if(cellPaddingText != null) {
220             try {
221                 cellPadding = Integer.parseInt(cellPaddingText);
222                 //TODO: Both cellpadding and cellspacing take percentages.
223
//TODO: Set cellpadding during layout, not in reset.
224
} catch(NumberFormatException JavaDoc nfe) {
225                 // ignore
226
}
227         }
228         ArrayList rows = this.ROWS;
229         ArrayList rowElements = this.ROW_ELEMENTS;
230         ArrayList allCells = this.ALL_CELLS;
231         Map rowElementToRowArray = new HashMap(2);
232         ArrayList cellList = te.getDescendents(COLUMNS_FILTER);
233         ArrayList currentNullRow = null;
234         Iterator ci = cellList.iterator();
235         while(ci.hasNext()) {
236             HTMLTableCellElementImpl columnNode = (HTMLTableCellElementImpl) ci.next();
237             HTMLTableRowElementImpl rowElement = this.getParentRow(columnNode);
238             ArrayList row;
239             if(rowElement != null) {
240                 currentNullRow = null;
241                 row = (ArrayList) rowElementToRowArray.get(rowElement);
242                 if(row == null) {
243                     row = new ArrayList();
244                     rowElementToRowArray.put(rowElement, row);
245                     rows.add(row);
246                     rowElements.add(rowElement);
247                 }
248             }
249             else {
250                 // Doesn't have a TR parent. Let's add a ROW just for itself.
251
// Both IE and FireFox have this behavior.
252
if(currentNullRow != null) {
253                     row = currentNullRow;
254                 }
255                 else {
256                     row = new ArrayList();
257                     currentNullRow = row;
258                     rows.add(row);
259                     // Null TR element must be added to match.
260
rowElements.add(null);
261                 }
262             }
263             RTableCell ac = (RTableCell) columnNode.getUINode();
264             if(ac == null) {
265                 // Saved UI nodes must be reused, because they
266
// can contain a collection of GUI components.
267
ac = new RTableCell(columnNode, this.parserContext, this.rendererContext, this.frameContext, this.container);
268                 ac.setParent(this.relement);
269                 columnNode.setUINode(ac);
270             }
271             ac.setCellPadding(cellPadding);
272             VirtualCell vc = new VirtualCell(ac, true);
273             ac.setTopLeftVirtualCell(vc);
274             row.add(vc);
275             allCells.add(ac);
276         }
277     }
278     
279     /**
280      * Based on colspans and rowspans, creates
281      * additional virtual cells from actual
282      * table cells.
283      */

284     private void adjustForCellSpans() {
285         ArrayList rows = this.ROWS;
286         int numRows = rows.size();
287         for(int r = 0; r < numRows; r++) {
288             ArrayList row = (ArrayList) rows.get(r);
289             int numCols = row.size();
290             for(int c = 0; c < numCols; c++) {
291                 VirtualCell vc = (VirtualCell) row.get(c);
292                 if(vc != null && vc.isTopLeft()) {
293                     RTableCell ac = vc.getActualCell();
294                     int colspan = ac.getColSpan();
295                     if(colspan < 1) {
296                         colspan = 1;
297                     }
298                     int rowspan = ac.getRowSpan();
299                     if(rowspan < 1) {
300                         rowspan = 1;
301                     }
302                     
303                     // Make sure row exists
304
int targetRows = r + rowspan;
305                     while(rows.size() < targetRows) {
306                         rows.add(new ArrayList());
307                     }
308                     
309                     numRows = rows.size();
310                     for(int y = 0; y < rowspan; y++) {
311                         if(colspan > 1 || y > 0) {
312                             // Get row
313
int nr = r + y;
314                             ArrayList newRow = (ArrayList) rows.get(nr);
315
316                             // Insert missing cells in row
317
int xstart = y == 0 ? 1 : 0;
318                                                         
319                             // Insert virtual cells, potentially
320
// shifting others to the right.
321
for(int cc = xstart; cc < colspan; cc++) {
322                                 int nc = c + cc;
323                                 while(newRow.size() < nc) {
324                                     newRow.add(null);
325                                 }
326                                 newRow.add(nc, new VirtualCell(ac, false));
327                             }
328                             if(row == newRow) {
329                                 numCols = row.size();
330                             }
331                         }
332                     }
333                 }
334             }
335         }
336
337         // Adjust row and column of virtual cells
338
for(int r = 0; r < numRows; r++) {
339             ArrayList row = (ArrayList) rows.get(r);
340             int numCols = row.size();
341             for(int c = 0; c < numCols; c++) {
342                 VirtualCell vc = (VirtualCell) row.get(c);
343                 if(vc != null) {
344                     vc.setColumn(c);
345                     vc.setRow(r);
346                 }
347             }
348         }
349     }
350
351     /**
352      * Populates the columnSizes and rowSizes arrays,
353      * setting htmlLength in each element.
354      */

355     private void createSizeArrays() {
356         ArrayList rows = this.ROWS;
357         int numRows = rows.size();
358         SizeInfo[] rowSizes = new SizeInfo[numRows];
359         this.rowSizes = rowSizes;
360         int numCols = 0;
361         ArrayList rowElements = this.ROW_ELEMENTS;
362         for(int i = 0; i < numRows; i++) {
363             ArrayList row = (ArrayList) rows.get(i);
364             int rs = row.size();
365             if(rs > numCols) {
366                 numCols = rs;
367             }
368             SizeInfo rowSizeInfo = new SizeInfo();
369             rowSizes[i] = rowSizeInfo;
370             HTMLTableRowElement rowElement;
371             try {
372                 rowElement = (HTMLTableRowElement) rowElements.get(i);
373                 // Possible rowElement is null because TD does not have TR parent
374
} catch(IndexOutOfBoundsException JavaDoc iob) {
375                 //Possible if rowspan expands beyond that
376
rowElement = null;
377             }
378             //TODO: TR.height an IE quirk?
379
String JavaDoc rowHeightText = rowElement == null ? null : rowElement.getAttribute("height");
380             HtmlLength rowHeightLength = null;
381             if(rowHeightText != null) {
382                 try {
383                     rowHeightLength = new HtmlLength(rowHeightText);
384                 } catch(Exception JavaDoc err) {
385                     // ignore
386
}
387             }
388             if(rowHeightLength != null) {
389                 rowSizeInfo.htmlLength = rowHeightLength;
390             }
391             else {
392                 HtmlLength bestHeightLength = null;
393                 for(int x = 0; x < rs; x++) {
394                     VirtualCell vc = (VirtualCell) row.get(x);
395                     if(vc != null) {
396                         HtmlLength vcHeightLength = vc.getHeightLength();
397                         if(vcHeightLength != null && vcHeightLength.isPreferredOver(bestHeightLength)) {
398                             bestHeightLength = vcHeightLength;
399                         }
400                     }
401                 }
402                 rowSizeInfo.htmlLength = bestHeightLength;
403             }
404         }
405         SizeInfo[] columnSizes = new SizeInfo[numCols];
406         this.columnSizes = columnSizes;
407         for(int i = 0; i < numCols; i++) {
408             HtmlLength bestWidthLength = null;
409             
410             // Cells with colspan==1 first.
411
for(int y = 0; y < numRows; y++) {
412                 ArrayList row = (ArrayList) rows.get(y);
413                 VirtualCell vc;
414                 try {
415                     vc = (VirtualCell) row.get(i);
416                 } catch(IndexOutOfBoundsException JavaDoc iob) {
417                     vc = null;
418                 }
419                 if(vc != null) {
420                     RTableCell ac = vc.getActualCell();
421                     if(ac.getColSpan() == 1) {
422                         HtmlLength vcWidthLength = vc.getWidthLength();
423                         if(vcWidthLength != null && vcWidthLength.isPreferredOver(bestWidthLength)) {
424                             bestWidthLength = vcWidthLength;
425                         }
426                     }
427                 }
428             }
429             // Now cells with colspan>1.
430
if(bestWidthLength == null) {
431                 for(int y = 0; y < numRows; y++) {
432                     ArrayList row = (ArrayList) rows.get(y);
433                     VirtualCell vc;
434                     try {
435                         vc = (VirtualCell) row.get(i);
436                     } catch(IndexOutOfBoundsException JavaDoc iob) {
437                         vc = null;
438                     }
439                     if(vc != null) {
440                         RTableCell ac = vc.getActualCell();
441                         if(ac.getColSpan() > 1) {
442                             HtmlLength vcWidthLength = vc.getWidthLength();
443                             if(vcWidthLength != null && vcWidthLength.isPreferredOver(bestWidthLength)) {
444                                 bestWidthLength = vcWidthLength;
445                             }
446                         }
447                     }
448                 }
449             }
450             SizeInfo colSizeInfo = new SizeInfo();
451             colSizeInfo.htmlLength = bestWidthLength;
452             columnSizes[i] = colSizeInfo;
453         }
454     }
455     
456     /**
457      * Determines the size of each column, and the table width.
458      * Does the following:
459      * <ol>
460      * <li>Determine tentative widths. This is done by looking
461      * at declared column widths, any table width, and
462      * filling in the blanks. No rendering is done.
463      * The tentative
464      * width of columns with no declared width is zero.
465      *
466      * <li>Render all cell blocks. It uses the tentative widths from
467      * the previous step as a desired width. The resulting width
468      * is considered a sort of minimum. If the column
469      * width is not defined, use a NOWRAP override flag to render.
470      *
471      * <li>Check if cell widths are too narrow for the rendered
472      * width. In the case of columns without a declared width,
473      * check if they are too wide.
474      *
475      * <li>Finally, adjust widths considering the expected max table
476      * size. Columns are layed out again if necessary to determine
477      * if they can really be shrunk.
478      * </ol>
479      * @param renderState
480      * @param border
481      * @param cellSpacingX
482      * @param cellSpacingY
483      * @param availWidth
484      */

485     private void determineColumnSizes(int hasBorder, int cellSpacingX, int cellSpacingY, int availWidth) {
486         HtmlLength tableWidthLength = this.tableWidthLength;
487         int tableWidth;
488         boolean widthKnown;
489         if(tableWidthLength != null) {
490             tableWidth = tableWidthLength.getLength(availWidth);
491             widthKnown = true;
492         }
493         else {
494             tableWidth = availWidth;
495             widthKnown = false;
496         }
497         SizeInfo[] columnSizes = this.columnSizes;
498         int widthsOfExtras = this.widthsOfExtras;
499         int cellAvailWidth = tableWidth - widthsOfExtras;
500         if(cellAvailWidth < 0) {
501             tableWidth += (-cellAvailWidth);
502             cellAvailWidth = 0;
503         }
504         
505         // Determine tentative column widths based on specified cell widths
506

507         this.determineTentativeSizes(columnSizes, widthsOfExtras, cellAvailWidth, widthKnown);
508         
509         // Pre-render cells. This will give the minimum width of each cell,
510
// in addition to the minimum height.
511

512         this.preLayout(hasBorder, cellSpacingX, cellSpacingY, widthKnown);
513
514         // Increases column widths if they are less than minimums of each cell.
515

516         this.adjustForRenderWidths(columnSizes, hasBorder, cellSpacingX, widthKnown);
517         
518         // Adjust for expected total width
519

520         this.adjustWidthsForExpectedMax(columnSizes, cellAvailWidth, widthKnown);
521     }
522     
523     /**
524      * This method sets the tentative actual sizes of columns (rows) based
525      * on specified witdhs (heights) if available.
526      * @param columnSizes
527      * @param widthsOfExtras
528      * @param cellAvailWidth
529      */

530     private void determineTentativeSizes(SizeInfo[] columnSizes, int widthsOfExtras, int cellAvailWidth, boolean setNoWidthColumns) {
531         int numCols = columnSizes.length;
532         
533         // Look at percentages first
534
int widthUsedByPercent = 0;
535         for(int i = 0; i < numCols; i++) {
536             SizeInfo colSizeInfo = columnSizes[i];
537             HtmlLength widthLength = colSizeInfo.htmlLength;
538             if(widthLength != null && widthLength.getLengthType() == HtmlLength.LENGTH) {
539                 int actualSizeInt = widthLength.getLength(cellAvailWidth);
540                 widthUsedByPercent += actualSizeInt;
541                 colSizeInfo.actualSize = actualSizeInt;
542             }
543         }
544         
545         // Look at columns with absolute sizes
546
int widthUsedByAbsolute = 0;
547         int numNoWidthColumns = 0;
548         for(int i = 0; i < numCols; i++) {
549             SizeInfo colSizeInfo = columnSizes[i];
550             HtmlLength widthLength = colSizeInfo.htmlLength;
551             if(widthLength != null && widthLength.getLengthType() != HtmlLength.LENGTH) {
552                 //TODO: MULTI-LENGTH not supported
553
int actualSizeInt = widthLength.getRawValue();
554                 widthUsedByAbsolute += actualSizeInt;
555                 colSizeInfo.actualSize = actualSizeInt;
556             }
557             else if(widthLength == null) {
558                 numNoWidthColumns++;
559             }
560         }
561         
562         // Tentative width of all columns without a declared
563
// width is set to zero. The pre-render will determine
564
// a better size.
565

566 // // Assign all columns without widths now
567
// int widthUsedByUnspecified = 0;
568
// if(setNoWidthColumns) {
569
// int remainingWidth = cellAvailWidth - widthUsedByAbsolute - widthUsedByPercent;
570
// if(remainingWidth > 0) {
571
// for(int i = 0; i < numCols; i++) {
572
// SizeInfo colSizeInfo = columnSizes[i];
573
// HtmlLength widthLength = colSizeInfo.htmlLength;
574
// if(widthLength == null) {
575
// int actualSizeInt = remainingWidth / numNoWidthColumns;
576
// widthUsedByUnspecified += actualSizeInt;
577
// colSizeInfo.actualSize = actualSizeInt;
578
// }
579
// }
580
// }
581
// }
582

583         // Contract if necessary. This is done again later, but this is
584
// an optimization, as it may prevent re-layout. It is only done
585
// if all columns have some kind of declared width.
586

587         if(numNoWidthColumns == 0) {
588             int totalWidthUsed = widthUsedByPercent + widthUsedByAbsolute;
589             int difference = totalWidthUsed - cellAvailWidth;
590             // See if absolutes need to be contracted
591
if(difference > 0) {
592                 if(widthUsedByAbsolute > 0) {
593                     int expectedAbsoluteWidthTotal = widthUsedByAbsolute - difference;
594                     if(expectedAbsoluteWidthTotal < 0) {
595                         expectedAbsoluteWidthTotal = 0;
596                     }
597                     double ratio = (double) expectedAbsoluteWidthTotal / widthUsedByAbsolute;
598                     for(int i = 0; i < numCols; i++) {
599                         SizeInfo sizeInfo = columnSizes[i];
600                         HtmlLength widthLength = columnSizes[i].htmlLength;
601                         if(widthLength != null && widthLength.getLengthType() != HtmlLength.LENGTH) {
602                             int oldActualSize = sizeInfo.actualSize;
603                             int newActualSize = (int) Math.round(oldActualSize * ratio);
604                             sizeInfo.actualSize = newActualSize;
605                             totalWidthUsed += (newActualSize - oldActualSize);
606                         }
607                     }
608                     difference = totalWidthUsed - cellAvailWidth;
609                 }
610
611                 // See if percentages need to be contracted
612
if(difference > 0) {
613                     if(widthUsedByPercent > 0) {
614                         int expectedPercentWidthTotal = widthUsedByPercent - difference;
615                         if(expectedPercentWidthTotal < 0) {
616                             expectedPercentWidthTotal = 0;
617                         }
618                         double ratio = (double) expectedPercentWidthTotal / widthUsedByPercent;
619                         for(int i = 0; i < numCols; i++) {
620                             SizeInfo sizeInfo = columnSizes[i];
621                             HtmlLength widthLength = columnSizes[i].htmlLength;
622                             if(widthLength != null && widthLength.getLengthType() == HtmlLength.LENGTH) {
623                                 int oldActualSize = sizeInfo.actualSize;
624                                 int newActualSize = (int) Math.round(oldActualSize * ratio);
625                                 sizeInfo.actualSize = newActualSize;
626                                 totalWidthUsed += (newActualSize - oldActualSize);
627                             }
628                         }
629                     }
630                 }
631             }
632         }
633     }
634     
635     /**
636      * Contracts column sizes according to render sizes.
637      */

638     private void adjustForRenderWidths(SizeInfo[] columnSizes, int hasBorder, int cellSpacing, boolean tableWidthKnown) {
639         int numCols = columnSizes.length;
640         for(int i = 0; i < numCols; i++) {
641             SizeInfo si = columnSizes[i];
642             if(si.actualSize < si.layoutSize) {
643                 si.actualSize = si.layoutSize;
644             }
645 // else if(si.htmlLength == null) {
646
// // For cells without a declared width, see if
647
// // their tentative width is a bit too big.
648
// if(si.actualSize > si.layoutSize) {
649
// si.actualSize = si.layoutSize;
650
// }
651
// }
652
}
653     }
654     
655     private void layoutColumn(SizeInfo[] columnSizes, SizeInfo colSize, int col, int cellSpacingX, int hasBorder) {
656         SizeInfo[] rowSizes = this.rowSizes;
657         ArrayList rows = this.ROWS;
658         int numRows = rows.size();
659         int actualSize = colSize.actualSize;
660         colSize.layoutSize = 0;
661         for(int row = 0; row < numRows;) {
662             //SizeInfo rowSize = rowSizes[row];
663
ArrayList columns = (ArrayList) rows.get(row);
664             VirtualCell vc = null;
665             try {
666                 vc = (VirtualCell) columns.get(col);
667             } catch(IndexOutOfBoundsException JavaDoc iob) {
668                 vc = null;
669             }
670             RTableCell ac = vc == null ? null : vc.getActualCell();
671             if(ac != null) {
672                 int colSpan = ac.getColSpan();
673                 if(colSpan > 1) {
674                     int firstCol = ac.getVirtualColumn();
675                     int cellExtras = (colSpan - 1) * (cellSpacingX + 2 * hasBorder);
676                     int vcActualWidth = cellExtras;
677                     for(int x = 0; x < colSpan; x++) {
678                         vcActualWidth += columnSizes[firstCol + x].actualSize;
679                     }
680                     //TODO: better height possible
681
Dimension JavaDoc size = ac.doCellLayout(vcActualWidth, 0, false, false);
682                     int vcRenderWidth = size.width;
683                     
684                     int denominator = (vcActualWidth - cellExtras);
685                     int newTentativeCellWidth;
686                     if(denominator > 0) {
687                         newTentativeCellWidth = actualSize * (vcRenderWidth - cellExtras) / denominator;
688                     }
689                     else {
690                         newTentativeCellWidth = (vcRenderWidth - cellExtras) / colSpan;
691                     }
692                     if(newTentativeCellWidth > colSize.layoutSize) {
693                         colSize.layoutSize = newTentativeCellWidth;
694                     }
695                     int rowSpan = ac.getRowSpan();
696                     int vch = (size.height - (rowSpan - 1) * (this.cellSpacingY + 2 * hasBorder)) / rowSpan;
697                     for(int y = 0; y < rowSpan; y++) {
698                         if(rowSizes[row + y].minSize < vch) {
699                             rowSizes[row + y].minSize = vch;
700                         }
701                     }
702                 }
703                 else {
704                     //TODO: better height possible
705
Dimension JavaDoc size = ac.doCellLayout(actualSize, 0, false, false);
706                     if(size.width > colSize.layoutSize) {
707                         colSize.layoutSize = size.width;
708                     }
709                     int rowSpan = ac.getRowSpan();
710                     int vch = (size.height - (rowSpan - 1) * (this.cellSpacingY + 2 * hasBorder)) / rowSpan;
711                     for(int y = 0; y < rowSpan; y++) {
712                         if(rowSizes[row + y].minSize < vch) {
713                             rowSizes[row + y].minSize = vch;
714                         }
715                     }
716                 }
717             }
718             row = (ac == null ? row + 1 : ac.getVirtualRow() + ac.getRowSpan());
719         }
720     }
721     
722     private int adjustWidthsForExpectedMax(SizeInfo[] columnSizes, int cellAvailWidth, boolean expand) {
723         int hasBorder = this.hasOldStyleBorder;
724         int cellSpacingX = this.cellSpacingX;
725         int currentTotal = 0;
726         int numCols = columnSizes.length;
727         for(int i = 0; i < numCols; i++) {
728             currentTotal += columnSizes[i].actualSize;
729         }
730         int difference = currentTotal - cellAvailWidth;
731         if(difference > 0 || (difference < 0 && expand)) {
732             // First, try to contract/expand columns with no width
733
int noWidthTotal = 0;
734             int numNoWidth = 0;
735             for(int i = 0; i < numCols; i++) {
736                 if(columnSizes[i].htmlLength == null) {
737                     numNoWidth++;
738                     noWidthTotal += columnSizes[i].actualSize;
739                 }
740             }
741             if(noWidthTotal > 0) {
742                 //TODO: This is not shrinking correctly.
743
int expectedNoWidthTotal = noWidthTotal - difference;
744                 if(expectedNoWidthTotal < 0) {
745                     expectedNoWidthTotal = 0;
746                 }
747                 double ratio = (double) expectedNoWidthTotal / noWidthTotal;
748                 int noWidthCount = 0;
749                 for(int i = 0; i < numCols; i++) {
750                     SizeInfo sizeInfo = columnSizes[i];
751                     if(sizeInfo.htmlLength == null) {
752                         int oldActualSize = sizeInfo.actualSize;
753                         int newActualSize;
754                         if(++noWidthCount == numNoWidth) {
755                             // Last column without a width.
756
int currentDiff = currentTotal - cellAvailWidth;
757                             newActualSize = oldActualSize - currentDiff;
758                             if(newActualSize < 0) {
759                                 newActualSize = 0;
760                             }
761                         }
762                         else {
763                             newActualSize = (int) Math.round(oldActualSize * ratio);
764                         }
765                         sizeInfo.actualSize = newActualSize;
766                         if(newActualSize < sizeInfo.layoutSize) {
767                             // See if it actually fits.
768
this.layoutColumn(columnSizes, sizeInfo, i, cellSpacingX, hasBorder);
769                             if(newActualSize < sizeInfo.layoutSize) {
770                                 // Didn't fit.
771
newActualSize = sizeInfo.layoutSize;
772                                 sizeInfo.actualSize = newActualSize;
773                             }
774                         }
775                         currentTotal += (newActualSize - oldActualSize);
776                     }
777                 }
778                 difference = currentTotal - cellAvailWidth;
779             }
780             
781             // See if absolutes need to be contracted
782
if(difference > 0 || (difference < 0 && expand)) {
783                 int absoluteWidthTotal = 0;
784                 for(int i = 0; i < numCols; i++) {
785                     HtmlLength widthLength = columnSizes[i].htmlLength;
786                     if(widthLength != null && widthLength.getLengthType() != HtmlLength.LENGTH) {
787                         absoluteWidthTotal += columnSizes[i].actualSize;
788                     }
789                 }
790                 if(absoluteWidthTotal > 0) {
791                     int expectedAbsoluteWidthTotal = absoluteWidthTotal - difference;
792                     if(expectedAbsoluteWidthTotal < 0) {
793                         expectedAbsoluteWidthTotal = 0;
794                     }
795                     double ratio = (double) expectedAbsoluteWidthTotal / absoluteWidthTotal;
796                     for(int i = 0; i < numCols; i++) {
797                         SizeInfo sizeInfo = columnSizes[i];
798                         HtmlLength widthLength = columnSizes[i].htmlLength;
799                         if(widthLength != null && widthLength.getLengthType() != HtmlLength.LENGTH) {
800                             int oldActualSize = sizeInfo.actualSize;
801                             int newActualSize = (int) Math.round(oldActualSize * ratio);
802                             sizeInfo.actualSize = newActualSize;
803                             if(newActualSize < sizeInfo.layoutSize) {
804                                 // See if it actually fits.
805
this.layoutColumn(columnSizes, sizeInfo, i, cellSpacingX, hasBorder);
806                                 if(newActualSize < sizeInfo.layoutSize) {
807                                     // Didn't fit.
808
newActualSize = sizeInfo.layoutSize;
809                                     sizeInfo.actualSize = newActualSize;
810                                 }
811                             }
812                             currentTotal += (newActualSize - oldActualSize);
813                         }
814                     }
815                     difference = currentTotal - cellAvailWidth;
816                 }
817                 
818                 // See if percentages need to be contracted
819
if(difference > 0 || (difference < 0 && expand)) {
820                     int percentWidthTotal = 0;
821                     for(int i = 0; i < numCols; i++) {
822                         HtmlLength widthLength = columnSizes[i].htmlLength;
823                         if(widthLength != null && widthLength.getLengthType() == HtmlLength.LENGTH) {
824                             percentWidthTotal += columnSizes[i].actualSize;
825                         }
826                     }
827                     if(percentWidthTotal > 0) {
828                         int expectedPercentWidthTotal = percentWidthTotal - difference;
829                         if(expectedPercentWidthTotal < 0) {
830                             expectedPercentWidthTotal = 0;
831                         }
832                         double ratio = (double) expectedPercentWidthTotal / percentWidthTotal;
833                         for(int i = 0; i < numCols; i++) {
834                             SizeInfo sizeInfo = columnSizes[i];
835                             HtmlLength widthLength = columnSizes[i].htmlLength;
836                             if(widthLength != null && widthLength.getLengthType() == HtmlLength.LENGTH) {
837                                 int oldActualSize = sizeInfo.actualSize;
838                                 int newActualSize = (int) Math.round(oldActualSize * ratio);
839                                 sizeInfo.actualSize = newActualSize;
840                                 if(newActualSize < sizeInfo.layoutSize) {
841                                     // See if it actually fits.
842
this.layoutColumn(columnSizes, sizeInfo, i, cellSpacingX, hasBorder);
843                                     if(newActualSize < sizeInfo.layoutSize) {
844                                         // Didn't fit.
845
newActualSize = sizeInfo.layoutSize;
846                                         sizeInfo.actualSize = newActualSize;
847                                     }
848                                 }
849                                 currentTotal += (newActualSize - oldActualSize);
850                             }
851                         }
852                     }
853                 }
854             }
855         }
856         return currentTotal;
857     }
858     
859     /**
860      * This method renders each cell using already set actual column widths.
861      * It sets minimum row heights based on this.
862      */

863     private final void preLayout(int hasBorder, int cellSpacingX, int cellSpacingY, boolean tableWidthKnown) {
864         //TODO: Fix for table without width that has a subtable with width=100%.
865
//TODO: Maybe it can be addressed when NOWRAP is implemented.
866
//TODO: Maybe it's possible to eliminate this pre-layout altogether.
867

868         SizeInfo[] colSizes = this.columnSizes;
869         SizeInfo[] rowSizes = this.rowSizes;
870
871         // Initialize minSize in rows
872
int numRows = rowSizes.length;
873         for(int i = 0; i < numRows; i++) {
874             rowSizes[i].minSize = 0;
875         }
876         
877         // Initialize layoutSize in columns
878
int numCols = colSizes.length;
879         for(int i = 0; i < numCols; i++) {
880             colSizes[i].layoutSize = 0;
881         }
882         
883         ArrayList allCells = this.ALL_CELLS;
884         int numCells = allCells.size();
885         for(int i = 0; i < numCells; i++) {
886             RTableCell cell = (RTableCell) allCells.get(i);
887             int col = cell.getVirtualColumn();
888             int colSpan = cell.getColSpan();
889             int cellsTotalWidth;
890             int cellsUsedWidth;
891             boolean widthDeclared = false;
892             if(colSpan > 1) {
893                 cellsUsedWidth = 0;
894                 for(int x = 0; x < colSpan; x++) {
895                     SizeInfo colSize = colSizes[col + x];
896                     if(colSize.htmlLength != null) {
897                         widthDeclared = true;
898                     }
899                     cellsUsedWidth += colSize.actualSize;
900                 }
901                 cellsTotalWidth = cellsUsedWidth + (colSpan - 1) * (cellSpacingX + 2 * hasBorder);
902             }
903             else {
904                 SizeInfo colSize = colSizes[col];
905                 if(colSize.htmlLength != null) {
906                     widthDeclared = true;
907                 }
908                 cellsUsedWidth = cellsTotalWidth = colSize.actualSize;
909             }
910
911             //TODO: A tentative height could be used here: Height of
912
//table divided by number of rows.
913

914             java.awt.Dimension JavaDoc size;
915             RenderThreadState state = RenderThreadState.getState();
916             boolean prevOverrideNoWrap = state.overrideNoWrap;
917             try {
918                 if(!prevOverrideNoWrap) {
919                     state.overrideNoWrap = !widthDeclared;
920                 }
921                 size = cell.doCellLayout(cellsTotalWidth, 0, false, false);
922             } finally {
923                 state.overrideNoWrap = prevOverrideNoWrap;
924             }
925             // Set render widths
926
int cellLayoutWidth = size.width;
927             if(colSpan > 1) {
928                 if(cellsUsedWidth > 0) {
929                     double ratio = (double) cellLayoutWidth / cellsUsedWidth;
930                     for(int x = 0; x < colSpan; x++) {
931                         SizeInfo si = colSizes[col + x];
932                         int newLayoutSize = (int) Math.round(si.actualSize * ratio);
933                         if(si.layoutSize < newLayoutSize) {
934                             si.layoutSize = newLayoutSize;
935                         }
936                     }
937                 }
938                 else {
939                     int newLayoutSize = cellLayoutWidth / colSpan;
940                     for(int x = 0; x < colSpan; x++) {
941                         SizeInfo si = colSizes[col + x];
942                         if(si.layoutSize < newLayoutSize) {
943                             si.layoutSize = newLayoutSize;
944                         }
945                     }
946                 }
947             }
948             else {
949                 SizeInfo colSizeInfo = colSizes[col];
950                 if(colSizeInfo.layoutSize < cellLayoutWidth) {
951                     colSizeInfo.layoutSize = cellLayoutWidth;
952                 }
953             }
954             
955             // Set minimum heights
956
int actualCellHeight = size.height;
957             int row = cell.getVirtualRow();
958             int rowSpan = cell.getRowSpan();
959             if(rowSpan > 1) {
960                 int vch = (actualCellHeight - (rowSpan - 1) * (cellSpacingY + 2 * hasBorder)) / rowSpan;
961                 for(int y = 0; y < rowSpan; y++) {
962                     if(rowSizes[row + y].minSize < vch) {
963                         rowSizes[row + y].minSize = vch;
964                     }
965                 }
966             }
967             else {
968                 if(rowSizes[row].minSize < actualCellHeight) {
969                     rowSizes[row].minSize = actualCellHeight;
970                 }
971             }
972         }
973     }
974
975     private void determineRowSizes(int hasBorder, int cellSpacing, int availHeight) {
976         HtmlLength tableHeightLength = TableMatrix.getHeightLength(this.tableElement, availHeight);
977         int tableHeight;
978         SizeInfo[] rowSizes = this.rowSizes;
979         int numRows = rowSizes.length;
980         int heightsOfExtras = this.heightsOfExtras;
981         if(tableHeightLength != null) {
982             tableHeight = tableHeightLength.getLength(availHeight);
983             this.determineRowSizesFixedTH(hasBorder, cellSpacing, availHeight, tableHeight);
984         }
985         else {
986             tableHeight = heightsOfExtras;
987             for(int row = 0; row < numRows; row++) {
988                 tableHeight += rowSizes[row].minSize;
989             }
990             this.determineRowSizesFlexibleTH(hasBorder, cellSpacing, availHeight);
991         }
992     }
993     
994     private void determineRowSizesFixedTH(int hasBorder, int cellSpacing, int availHeight, int tableHeight) {
995         SizeInfo[] rowSizes = this.rowSizes;
996         int numRows = rowSizes.length;
997         int heightsOfExtras = this.heightsOfExtras;
998         int cellAvailHeight = tableHeight - heightsOfExtras;
999         if(cellAvailHeight < 0) {
1000            cellAvailHeight = 0;
1001        }
1002        
1003        // Look at percentages first
1004

1005        int heightUsedbyPercent = 0;
1006        int otherMinSize = 0;
1007        for(int i = 0; i < numRows; i++) {
1008            SizeInfo rowSizeInfo = rowSizes[i];
1009            HtmlLength heightLength = rowSizeInfo.htmlLength;
1010            if(heightLength != null && heightLength.getLengthType() == HtmlLength.LENGTH) {
1011                int actualSizeInt = heightLength.getLength(cellAvailHeight);
1012                if(actualSizeInt < rowSizeInfo.minSize) {
1013                    actualSizeInt = rowSizeInfo.minSize;
1014                }
1015                heightUsedbyPercent += actualSizeInt;
1016                rowSizeInfo.actualSize = actualSizeInt;
1017            }
1018            else {
1019                otherMinSize += rowSizeInfo.minSize;
1020            }
1021        }
1022        
1023        // Check if rows with percent are bigger than they should be
1024

1025        if(heightUsedbyPercent + otherMinSize > cellAvailHeight) {
1026            double ratio = (double) (cellAvailHeight - otherMinSize) / heightUsedbyPercent;
1027            for(int i = 0; i < numRows; i++) {
1028                SizeInfo rowSizeInfo = rowSizes[i];
1029                HtmlLength heightLength = rowSizeInfo.htmlLength;
1030                if(heightLength != null && heightLength.getLengthType() == HtmlLength.LENGTH) {
1031                    int actualSize = rowSizeInfo.actualSize;
1032                    int prevActualSize = actualSize;
1033                    int newActualSize = (int) Math.round(prevActualSize * ratio);
1034                    if(newActualSize < rowSizeInfo.minSize) {
1035                        newActualSize = rowSizeInfo.minSize;
1036                    }
1037                    heightUsedbyPercent += (newActualSize - prevActualSize);
1038                    rowSizeInfo.actualSize = newActualSize;
1039                }
1040            }
1041        }
1042        
1043        // Look at rows with absolute sizes
1044

1045        int heightUsedByAbsolute = 0;
1046        int noHeightMinSize = 0;
1047        int numNoHeightColumns = 0;
1048        for(int i = 0; i < numRows; i++) {
1049            SizeInfo rowSizeInfo = rowSizes[i];
1050            HtmlLength heightLength = rowSizeInfo.htmlLength;
1051            if(heightLength != null && heightLength.getLengthType() != HtmlLength.LENGTH) {
1052                //TODO: MULTI-LENGTH not supported
1053
int actualSizeInt = heightLength.getRawValue();
1054                if(actualSizeInt < rowSizeInfo.minSize) {
1055                    actualSizeInt = rowSizeInfo.minSize;
1056                }
1057                heightUsedByAbsolute += actualSizeInt;
1058                rowSizeInfo.actualSize = actualSizeInt;
1059            }
1060            else if(heightLength == null) {
1061                numNoHeightColumns++;
1062                noHeightMinSize += rowSizeInfo.minSize;
1063            }
1064        }
1065        
1066        // Check if absolute sizing is too much
1067

1068        if(heightUsedByAbsolute + heightUsedbyPercent + noHeightMinSize > cellAvailHeight) {
1069            double ratio = (double) (cellAvailHeight - noHeightMinSize - heightUsedbyPercent) / heightUsedByAbsolute;
1070            for(int i = 0; i < numRows; i++) {
1071                SizeInfo rowSizeInfo = rowSizes[i];
1072                HtmlLength heightLength = rowSizeInfo.htmlLength;
1073                if(heightLength != null && heightLength.getLengthType() != HtmlLength.LENGTH) {
1074                    int actualSize = rowSizeInfo.actualSize;
1075                    int prevActualSize = actualSize;
1076                    int newActualSize = (int) Math.round(prevActualSize * ratio);
1077                    if(newActualSize < rowSizeInfo.minSize) {
1078                        newActualSize = rowSizeInfo.minSize;
1079                    }
1080                    heightUsedByAbsolute += (newActualSize - prevActualSize);
1081                    rowSizeInfo.actualSize = newActualSize;
1082                }
1083            }
1084        }
1085        
1086        // Assign all rows without heights now
1087

1088        int remainingHeight = cellAvailHeight - heightUsedByAbsolute - heightUsedbyPercent;
1089        int heightUsedByRemaining = 0;
1090        for(int i = 0; i < numRows; i++) {
1091            SizeInfo rowSizeInfo = rowSizes[i];
1092            HtmlLength heightLength = rowSizeInfo.htmlLength;
1093            if(heightLength == null) {
1094                int actualSizeInt = remainingHeight / numNoHeightColumns;
1095                if(actualSizeInt < rowSizeInfo.minSize) {
1096                    actualSizeInt = rowSizeInfo.minSize;
1097                }
1098                heightUsedByRemaining += actualSizeInt;
1099                rowSizeInfo.actualSize = actualSizeInt;
1100            }
1101        }
1102
1103        // Calculate actual table width
1104

1105        int totalUsed = heightUsedByAbsolute + heightUsedbyPercent + heightUsedByRemaining;
1106        if(totalUsed >= cellAvailHeight) {
1107            this.tableHeight = totalUsed + heightsOfExtras;
1108        }
1109        else {
1110            // Rows too short; expand them
1111
double ratio = (double) cellAvailHeight / totalUsed;
1112            for(int i = 0; i < numRows; i++) {
1113                SizeInfo rowSizeInfo = rowSizes[i];
1114                int actualSize = rowSizeInfo.actualSize;
1115                rowSizeInfo.actualSize = (int) Math.round(actualSize * ratio);
1116            }
1117            this.tableHeight = tableHeight;
1118        }
1119        
1120        //TODO:
1121
// This final render is probably unnecessary. Avoid exponential rendering
1122
// by setting a single height of subcell. Verify that IE only sets height
1123
// of subcells when height of row or table are specified.
1124

1125        this.finalRender(hasBorder, cellSpacing);
1126    }
1127
1128    private void determineRowSizesFlexibleTH(int hasBorder, int cellSpacing, int availHeight) {
1129        SizeInfo[] rowSizes = this.rowSizes;
1130        int numRows = rowSizes.length;
1131        int heightsOfExtras = this.heightsOfExtras;
1132        
1133        // Look at rows with absolute sizes
1134
int heightUsedByAbsolute = 0;
1135        int percentSum = 0;
1136        for(int i = 0; i < numRows; i++) {
1137            SizeInfo rowSizeInfo = rowSizes[i];
1138            HtmlLength heightLength = rowSizeInfo.htmlLength;
1139            if(heightLength != null && heightLength.getLengthType() == HtmlLength.PIXELS) {
1140                //TODO: MULTI-LENGTH not supported
1141
int actualSizeInt = heightLength.getRawValue();
1142                if(actualSizeInt < rowSizeInfo.minSize) {
1143                    actualSizeInt = rowSizeInfo.minSize;
1144                }
1145                heightUsedByAbsolute += actualSizeInt;
1146                rowSizeInfo.actualSize = actualSizeInt;
1147            }
1148            else if(heightLength != null && heightLength.getLengthType() == HtmlLength.LENGTH) {
1149                percentSum += heightLength.getRawValue();
1150            }
1151        }
1152            
1153        // Look at rows with no specified heights
1154
int heightUsedByNoSize = 0;
1155        
1156        // Set sizes to in row height
1157
for(int i = 0; i < numRows; i++) {
1158            SizeInfo rowSizeInfo = rowSizes[i];
1159            HtmlLength widthLength = rowSizeInfo.htmlLength;
1160            if(widthLength == null) {
1161                int actualSizeInt = rowSizeInfo.minSize;
1162                heightUsedByNoSize += actualSizeInt;
1163                rowSizeInfo.actualSize = actualSizeInt;
1164            }
1165        }
1166        
1167        // Calculate actual total cell width
1168
int expectedTotalCellHeight = (int) Math.round((heightUsedByAbsolute + heightUsedByNoSize) / (1 - (percentSum / 100.0)));
1169        
1170        // Set widths of columns with percentages
1171
int heightUsedByPercent = 0;
1172        for(int i = 0; i < numRows; i++) {
1173            SizeInfo rowSizeInfo = rowSizes[i];
1174            HtmlLength heightLength = rowSizeInfo.htmlLength;
1175            if(heightLength != null && heightLength.getLengthType() == HtmlLength.LENGTH) {
1176                int actualSizeInt = heightLength.getLength(expectedTotalCellHeight);
1177                if(actualSizeInt < rowSizeInfo.minSize) {
1178                    actualSizeInt = rowSizeInfo.minSize;
1179                }
1180                heightUsedByPercent += actualSizeInt;
1181                rowSizeInfo.actualSize = actualSizeInt;
1182            }
1183        }
1184                        
1185        // Set width of table
1186
this.tableHeight = heightUsedByAbsolute + heightUsedByNoSize + heightUsedByPercent + heightsOfExtras;
1187        
1188        // Do a final render to set actual cell sizes
1189
this.finalRender(hasBorder, cellSpacing);
1190    }
1191
1192    /**
1193     * This method renders each cell using already set actual column widths.
1194     * It sets minimum row heights based on this.
1195     */

1196    private final void finalRender(int hasBorder, int cellSpacing) {
1197        // finalRender needs to adjust actualSize of columns and rows
1198
// given that things might change as we render one last time.
1199
ArrayList allCells = this.ALL_CELLS;
1200        SizeInfo[] colSizes = this.columnSizes;
1201        SizeInfo[] rowSizes = this.rowSizes;
1202        int numCells = allCells.size();
1203        for(int i = 0; i < numCells; i++) {
1204            RTableCell cell = (RTableCell) allCells.get(i);
1205            int col = cell.getVirtualColumn();
1206            int colSpan = cell.getColSpan();
1207            int totalCellWidth;
1208            if(colSpan > 1) {
1209                totalCellWidth = (colSpan - 1) * (cellSpacing + 2 * hasBorder);
1210                for(int x = 0; x < colSpan; x++) {
1211                    totalCellWidth += colSizes[col + x].actualSize;
1212                }
1213            }
1214            else {
1215                totalCellWidth = colSizes[col].actualSize;
1216            }
1217            int row = cell.getVirtualRow();
1218            int rowSpan = cell.getRowSpan();
1219            int totalCellHeight;
1220            if(rowSpan > 1) {
1221                totalCellHeight = (rowSpan - 1) * (cellSpacing + 2 * hasBorder);
1222                for(int y = 0; y < rowSpan; y++) {
1223                    totalCellHeight += rowSizes[row + y].actualSize;
1224                }
1225            }
1226            else {
1227                totalCellHeight = rowSizes[row].actualSize;
1228            }
1229            Dimension JavaDoc size = cell.doCellLayout(totalCellWidth, totalCellHeight, true, true);
1230            if(size.width > totalCellWidth) {
1231                if(colSpan == 1) {
1232                    if(size.width > colSizes[col].actualSize) {
1233                        colSizes[col].actualSize = size.width;
1234                    }
1235                }
1236                else {
1237                    //TODO: Adjustment for colSpan > 1.
1238
}
1239            }
1240            if(size.height > totalCellHeight) {
1241                if(rowSpan == 1) {
1242                    if(size.height > rowSizes[row].actualSize) {
1243                        rowSizes[row].actualSize = size.height;
1244                    }
1245                }
1246                else {
1247                    //TODO: Adjustment for rowSpan > 1.
1248
}
1249            }
1250        }
1251    }
1252
1253    /**
1254     * Sets bounds of each cell's component, and sumps up table width
1255     * and height.
1256     */

1257    public final void doLayout(Insets JavaDoc insets) {
1258        
1259        // Set row offsets
1260

1261        SizeInfo[] rowSizes = this.rowSizes;
1262        int numRows = rowSizes.length;
1263        int yoffset = insets.top;
1264        int cellSpacingY = this.cellSpacingY;
1265        int hasBorder = this.hasOldStyleBorder;
1266        for(int i = 0; i < numRows; i++) {
1267            yoffset += cellSpacingY;
1268            yoffset += hasBorder;
1269            SizeInfo rowSizeInfo = rowSizes[i];
1270            rowSizeInfo.offset = yoffset;
1271            yoffset += rowSizeInfo.actualSize;
1272            yoffset += hasBorder;
1273        }
1274        this.tableHeight = yoffset + cellSpacingY + insets.bottom;
1275        
1276        // Set colum offsets
1277

1278        SizeInfo[] colSizes = this.columnSizes;
1279        int numColumns = colSizes.length;
1280        int xoffset = insets.left;
1281        int cellSpacingX = this.cellSpacingX;
1282        for(int i = 0; i < numColumns; i++) {
1283            xoffset += cellSpacingX;
1284            xoffset += hasBorder;
1285            SizeInfo colSizeInfo = colSizes[i];
1286            colSizeInfo.offset = xoffset;
1287            xoffset += colSizeInfo.actualSize;
1288            xoffset += hasBorder;
1289        }
1290        this.tableWidth = xoffset + cellSpacingX + insets.right;
1291        
1292        // Set offsets of each cell
1293

1294        ArrayList allCells = this.ALL_CELLS;
1295        int numCells = allCells.size();
1296        for(int i = 0; i < numCells; i++) {
1297            RTableCell cell = (RTableCell) allCells.get(i);
1298            cell.setCellBounds(colSizes, rowSizes, hasBorder, cellSpacingX, cellSpacingY);
1299        }
1300    }
1301    
1302    public final void paint(Graphics JavaDoc g, Dimension JavaDoc size) {
1303        ArrayList allCells = this.ALL_CELLS;
1304        int numCells = allCells.size();
1305        for(int i = 0; i < numCells; i++) {
1306            RTableCell cell = (RTableCell) allCells.get(i);
1307            // Should clip table cells, just in case.
1308
Graphics JavaDoc newG = g.create(cell.x, cell.y, cell.width, cell.height);
1309            try {
1310                cell.paint(newG);
1311            } finally {
1312                newG.dispose();
1313            }
1314        }
1315
1316        if(this.hasOldStyleBorder > 0) {
1317// // Paint table border
1318
//
1319
// int tableWidth = this.tableWidth;
1320
// int tableHeight = this.tableHeight;
1321
// g.setColor(Color.BLACK); //TODO: Actual border color
1322
// int x = insets.left;
1323
// int y = insets.top;
1324
// for(int i = 0; i < border; i++) {
1325
// g.drawRect(x + i, y + i, tableWidth - i * 2 - 1, tableHeight - i * 2 - 1);
1326
// }
1327

1328            // Paint cell borders
1329

1330            g.setColor(Color.GRAY);
1331            for(int i = 0; i < numCells; i++) {
1332                RTableCell cell = (RTableCell) allCells.get(i);
1333                int cx = cell.getX() - 1;
1334                int cy = cell.getY() - 1;
1335                int cwidth = cell.getWidth() + 1;
1336                int cheight = cell.getHeight() + 1;
1337                g.drawRect(cx, cy, cwidth, cheight);
1338            }
1339        }
1340    }
1341
1342// public boolean paintSelection(Graphics g, boolean inSelection, RenderableSpot startPoint, RenderableSpot endPoint) {
1343
// ArrayList allCells = this.ALL_CELLS;
1344
// int numCells = allCells.size();
1345
// for(int i = 0; i < numCells; i++) {
1346
// RTableCell cell = (RTableCell) allCells.get(i);
1347
// Rectangle bounds = cell.getBounds();
1348
// int offsetX = bounds.x;
1349
// int offsetY = bounds.y;
1350
// g.translate(offsetX, offsetY);
1351
// try {
1352
// boolean newInSelection = cell.paintSelection(g, inSelection, startPoint, endPoint);
1353
// if(inSelection && !newInSelection) {
1354
// return false;
1355
// }
1356
// inSelection = newInSelection;
1357
// } finally {
1358
// g.translate(-offsetX, -offsetY);
1359
// }
1360
// }
1361
// return inSelection;
1362
// }
1363
//
1364
// public boolean extractSelectionText(StringBuffer buffer, boolean inSelection, RenderableSpot startPoint, RenderableSpot endPoint) {
1365
// ArrayList allCells = this.ALL_CELLS;
1366
// int numCells = allCells.size();
1367
// for(int i = 0; i < numCells; i++) {
1368
// RTableCell cell = (RTableCell) allCells.get(i);
1369
// boolean newInSelection = cell.extractSelectionText(buffer, inSelection, startPoint, endPoint);
1370
// if(inSelection && !newInSelection) {
1371
// return false;
1372
// }
1373
// inSelection = newInSelection;
1374
// }
1375
// return inSelection;
1376
// }
1377

1378    /* (non-Javadoc)
1379     * @see org.xamjwg.html.renderer.BoundableRenderable#getRenderablePoint(int, int)
1380     */

1381    public RenderableSpot getLowestRenderableSpot(int x, int y) {
1382        ArrayList allCells = this.ALL_CELLS;
1383        int numCells = allCells.size();
1384        for(int i = 0; i < numCells; i++) {
1385            RTableCell cell = (RTableCell) allCells.get(i);
1386            Rectangle JavaDoc bounds = cell.getBounds();
1387            if(bounds.contains(x, y)) {
1388                RenderableSpot rp = cell.getLowestRenderableSpot(x - bounds.x, y - bounds.y);
1389                if(rp != null) {
1390                    return rp;
1391                }
1392            }
1393        }
1394        return null;
1395    }
1396
1397    /* (non-Javadoc)
1398     * @see org.xamjwg.html.renderer.BoundableRenderable#onMouseClick(java.awt.event.MouseEvent, int, int)
1399     */

1400    public boolean onMouseClick(MouseEvent JavaDoc event, int x, int y) {
1401        ArrayList allCells = this.ALL_CELLS;
1402        int numCells = allCells.size();
1403        for(int i = 0; i < numCells; i++) {
1404            RTableCell cell = (RTableCell) allCells.get(i);
1405            Rectangle JavaDoc bounds = cell.getBounds();
1406            if(bounds.contains(x, y)) {
1407                if(!cell.onMouseClick(event, x - bounds.x, y - bounds.y)) {
1408                    return false;
1409                }
1410                break;
1411            }
1412        }
1413        return true;
1414    }
1415
1416    public boolean onDoubleClick(MouseEvent JavaDoc event, int x, int y) {
1417        ArrayList allCells = this.ALL_CELLS;
1418        int numCells = allCells.size();
1419        for(int i = 0; i < numCells; i++) {
1420            RTableCell cell = (RTableCell) allCells.get(i);
1421            Rectangle JavaDoc bounds = cell.getBounds();
1422            if(bounds.contains(x, y)) {
1423                if(!cell.onDoubleClick(event, x - bounds.x, y - bounds.y)) {
1424                    return false;
1425                }
1426                break;
1427            }
1428        }
1429        return true;
1430    }
1431
1432    private BoundableRenderable armedRenderable;
1433    
1434    /* (non-Javadoc)
1435     * @see org.xamjwg.html.renderer.BoundableRenderable#onMouseDisarmed(java.awt.event.MouseEvent)
1436     */

1437    public boolean onMouseDisarmed(MouseEvent JavaDoc event) {
1438        BoundableRenderable ar = this.armedRenderable;
1439        if(ar != null) {
1440            this.armedRenderable = null;
1441            return ar.onMouseDisarmed(event);
1442        }
1443        else {
1444            return true;
1445        }
1446    }
1447
1448    /* (non-Javadoc)
1449     * @see org.xamjwg.html.renderer.BoundableRenderable#onMousePressed(java.awt.event.MouseEvent, int, int)
1450     */

1451    public boolean onMousePressed(MouseEvent JavaDoc event, int x, int y) {
1452        ArrayList allCells = this.ALL_CELLS;
1453        int numCells = allCells.size();
1454        for(int i = 0; i < numCells; i++) {
1455            RTableCell cell = (RTableCell) allCells.get(i);
1456            Rectangle JavaDoc bounds = cell.getBounds();
1457            if(bounds.contains(x, y)) {
1458                if(!cell.onMousePressed(event, x - bounds.x, y - bounds.y)) {
1459                    this.armedRenderable = cell;
1460                    return false;
1461                }
1462                break;
1463            }
1464        }
1465        return true;
1466    }
1467
1468    /* (non-Javadoc)
1469     * @see org.xamjwg.html.renderer.BoundableRenderable#onMouseReleased(java.awt.event.MouseEvent, int, int)
1470     */

1471    public boolean onMouseReleased(MouseEvent JavaDoc event, int x, int y) {
1472        ArrayList allCells = this.ALL_CELLS;
1473        int numCells = allCells.size();
1474        boolean found = false;
1475        for(int i = 0; i < numCells; i++) {
1476            RTableCell cell = (RTableCell) allCells.get(i);
1477            Rectangle JavaDoc bounds = cell.getBounds();
1478            if(bounds.contains(x, y)) {
1479                found = true;
1480                BoundableRenderable oldArmedRenderable = this.armedRenderable;
1481                if(oldArmedRenderable != null && cell != oldArmedRenderable) {
1482                    oldArmedRenderable.onMouseDisarmed(event);
1483                    this.armedRenderable = null;
1484                }
1485                if(!cell.onMouseReleased(event, x - bounds.x, y - bounds.y)) {
1486                    return false;
1487                }
1488                break;
1489            }
1490        }
1491        if(!found) {
1492            BoundableRenderable oldArmedRenderable = this.armedRenderable;
1493            if(oldArmedRenderable != null) {
1494                oldArmedRenderable.onMouseDisarmed(event);
1495                this.armedRenderable = null;
1496            }
1497        }
1498        return true;
1499    }
1500
1501    public Iterator getRenderables() {
1502        return this.ALL_CELLS.iterator();
1503    }
1504
1505    private static class RowsFilter implements NodeFilter {
1506        public final boolean accept(Node JavaDoc node) {
1507            return (node instanceof HTMLTableRowElement);
1508        }
1509    }
1510
1511    private static class ColumnsFilter implements NodeFilter {
1512        public final boolean accept(Node JavaDoc node) {
1513            return (node instanceof HTMLTableCellElement);
1514        }
1515    }
1516    
1517    public static class SizeInfo {
1518        public HtmlLength htmlLength;
1519        public int actualSize;
1520        public int layoutSize;
1521        public int minSize;
1522        public int offset;
1523    }
1524}
1525
Popular Tags