Source Code Cross Referenced for TableBox.java in  » IDE-Netbeans » visualweb.api.designer » org » netbeans » modules » visualweb » css2 » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » visualweb.api.designer » org.netbeans.modules.visualweb.css2 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:        package org.netbeans.modules.visualweb.css2;
0042:
0043:        import java.util.List;
0044:        import org.netbeans.modules.visualweb.api.designer.cssengine.CssComputedValue;
0045:        import org.netbeans.modules.visualweb.api.designer.cssengine.CssProvider;
0046:        import org.netbeans.modules.visualweb.api.designer.cssengine.CssValue;
0047:        import org.netbeans.modules.visualweb.api.designer.markup.MarkupService;
0048:        import org.netbeans.modules.visualweb.designer.CssUtilities;
0049:        import org.netbeans.modules.visualweb.spi.designer.Decoration;
0050:        import java.awt.Cursor;
0051:        import java.awt.Graphics;
0052:        import java.awt.Insets;
0053:        import java.util.ArrayList;
0054:
0055:        import org.openide.ErrorManager;
0056:        import org.w3c.dom.Element;
0057:        import org.w3c.dom.Node;
0058:        import org.w3c.dom.NodeList;
0059:
0060:        import org.netbeans.modules.visualweb.designer.Interaction;
0061:        import org.netbeans.modules.visualweb.designer.TableResizer;
0062:        import org.netbeans.modules.visualweb.designer.WebForm;
0063:        import org.netbeans.modules.visualweb.api.designer.cssengine.XhtmlCss;
0064:        import org.netbeans.modules.visualweb.designer.html.HtmlAttribute;
0065:
0066:        /**
0067:         * TableBox represents a <table> element.
0068:         * <p>
0069:         *
0070:         * @todo Study http://www.mozilla.org/newlayout/doc/table-layout.html
0071:         *   and see if we can improve this class a bit.
0072:         * @todo Cell padding and cell spacing should be working; however,
0073:         *   there are a couple of off-by-one errors, which means that small
0074:         *   cell padding (1,2,3) does not look entirely right - clean this up.
0075:         * @todo Gotta add more safety-checking. For example, the user can
0076:         *   probably break things by specifying illegal (too large, or negative)
0077:         *   colspan or rowspan attributes, or in the fixed table layout, include
0078:         *   more columns than are indicated in the first row or with col
0079:         *   elements.
0080:         * @todo RFC 1942 suggests that the COLS attribute on a TABLE element
0081:         *    can set the number of columns, and force fixed layout. Check whether
0082:         *    this is still the case (and if it should be supported).
0083:         * @todo Colspan work: when computing the number of columns, the column
0084:         *    count might be affected by overlapping rowspans - e.g. even though
0085:         *    row two only has a single td, the previous row might have a rowspan
0086:         *    which blocks the first column, so it creates a new column. See if
0087:         *    Mozilla handles this, and if so, change creation code to resize
0088:         *    the cell table and adjust the column count if so.
0089:         * @todo Collapsed borders needs more work. Here's a potentially useful
0090:         *    resource: http://fantasai.inkedblade.net/style/discuss/collapsed-outer-border/
0091:         *    Note also that border types "inset" and "outset" have different
0092:         *    meanings in the collapsed border model - see 17.6.3
0093:         * @todo Scale percentages:
0094:         <pre>
0095:         if (percentTotals > 100) {
0096:         for (int i = 0; i < percentChildren.length; i++) {
0097:         if (percentChildren[i] > 0) {
0098:         percentChildren[i] =
0099:         (percentChildren[i] * 100) / percentTotals;
0100:         }
0101:         }
0102:         percentTotals = 100;
0103:         }
0104:         </pre>
0105:         * @todo Handle relative lengths (*, 2*, 3*), like I do in FrameSetBox.
0106:         * @todo Alignment: Look at this URL:
0107:         *     http://www.nic.fi/~tapio1/Teaching/Taulukot0.php3
0108:         *    and make sure I don't have those problems.
0109:         * @todo Support proportional width definitions:
0110:         *     http://www.w3.org/TR/html401/struct/tables.html#edef-TABLE
0111:         * @todo Ensure that if the table is positioned, the CaptionedTableBox
0112:         *     is repositioned, not just the table!
0113:         * @todo Override CssBox.find here; for a table we can a lot more efficiently
0114:         *     identify the box under a coordinate for our children since we know
0115:         *     the exact table layout.
0116:         * @todo Handle division by zero problem in my "spread out width" code
0117:         *     to deal with empty content tables.
0118:         * @todo Handle the BORDERCOLOR, BORDERCOLORLIGHT and BORDERCOLORDARK attributes.
0119:         * @todo Handle align/valign on the td's.
0120:         * @todo Ensure that th's are handled correctly.
0121:         * @todo Empty table cells should not get a cell border - they currently
0122:         *        do
0123:         *
0124:         * @author Tor Norbye
0125:         */
0126:        public class TableBox extends ContainerBox {
0127:            /** Special marker in cell positions which simply indicate that
0128:             * the cell is occupied by some previous cell with a colspan or
0129:             * or rowspan greater than 1.
0130:             */
0131:            private static final CellBox OCCUPIED = new OccupiedBox();
0132:            private static final int BORDER_RESIZE_DISTANCE = 5;
0133:
0134:            /** The element for the &lt;table&gt; tag itself.*/
0135:            //    private RaveElement table;
0136:            private Element table;
0137:
0138:            /** Number of columns in the table. */
0139:            private int columns = -1;
0140:
0141:            /** Number of rows in the table */
0142:            private int rows = -1;
0143:
0144:            //    /** Table Design Info. Can be null for table components that don't
0145:            //     *  implement this. */
0146:            //    private MarkupTableDesignInfo tableDesignInfo;
0147:            private int cellPadding = 1; // What is mozilla's default?
0148:            private int cellSpacing = 1; // What is mozilla's default?
0149:            private int borderWidth = 0;
0150:
0151:            /** The HTML4 spec says the default frame value is "void" but that does
0152:             * not seem to be what Mozilla does. */
0153:            private int frame = CssBorder.FRAME_UNSET;
0154:            private int rules = CssBorder.FRAME_UNSET;
0155:
0156:            /** Which kind of layout to use: fixed algorithm, or auto-layout algorithm.
0157:             * This is chosen bvased on the value of the css property "table-layout" as
0158:             * well as depending on whether the width property is set.
0159:             */
0160:            private boolean fixedLayout;
0161:
0162:            /** Array of cells holding boxes for each cell. Some cells may be empty
0163:             * (e.g. where the table does not specify all the cells, or has colspans/
0164:             * rowspans > 1.)
0165:             */
0166:            private CellBox[][] cells;
0167:
0168:            /** The rowspan for a given cell. If 0, it means uninitialized so use 1, the
0169:             * default, instead. */
0170:            private int[][] rowspans;
0171:
0172:            /** The colspan for a given cell. If 0, it means uninitialized so use 1, the
0173:             * default, instead. */
0174:            private int[][] colspans;
0175:
0176:            /** The row elements. Used for CSS lookups of row heights. */
0177:            private Element[] rowElements;
0178:
0179:            /** Computed height for each row, after layout */
0180:            private int[] rowHeights;
0181:
0182:            /** Sidedoor flag for column computation routine such that
0183:             * it knows to pick the column maxes rather than look at the containing
0184:             * block when picking final column widths. */
0185:            private boolean computingPrefWidth = false;
0186:
0187:            /** List of caption boxes, if any */
0188:            private List<CssBox> captionBoxes;
0189:
0190:            /** Applies only when there is a caption: if true, place caption above, otherwise below table */
0191:            private boolean captionAbove;
0192:
0193:            /** X coordinate of the table itself. 0 when there is no caption, otherwise possibly
0194:             * offset.
0195:             */
0196:            private int tableLeft;
0197:
0198:            /** Y coordinate of the table itself. 0 when there is no caption, otherwise possibly
0199:             * offset when the caption is above the table.
0200:             */
0201:            private int tableTop;
0202:
0203:            /** X coordinate of the table right hand side of the table. 0 when there is no caption,
0204:             * otherwise possibly offset (this will be the case when the caption is wider than the
0205:             * text.
0206:             */
0207:            private int tableRight;
0208:
0209:            /** Y coordinate of the bottom of the table itself. 0 when there is no caption,
0210:             * otherwise possibly offset when the caption is below the table.
0211:             */
0212:            private int tableBottom;
0213:
0214:            // If you add additional arrays here, make sure you update the swapRow()
0215:            // method to include it
0216:
0217:            /**
0218:             *  Create a TableBox representing a table for the given element
0219:             *
0220:             * @param element The table element
0221:             * @todo A table is never replaced, is it? Can we get rid of that
0222:             *   parameter?
0223:             */
0224:            private TableBox(WebForm webform, Element element, BoxType boxType,
0225:                    boolean inline, boolean replaced) {
0226:                super (webform, element, boxType, inline, replaced);
0227:                //        this.table = (RaveElement)element;
0228:                this .table = element;
0229:            }
0230:
0231:            /** Factory for creating a table */
0232:            public static CssBox getTableBox(WebForm webform, Element element,
0233:                    BoxType boxType, boolean inline, boolean replaced) {
0234:                return new TableBox(webform, element, boxType, inline, replaced);
0235:            }
0236:
0237:            /**
0238:             * {@inheritDoc}
0239:             *
0240:             * Specialized in TableBox to handle centering, since when centering is in effect
0241:             * we should assume a minimal table width (shrink to fit) and change the margins
0242:             * to be auto
0243:             */
0244:            protected void computeHorizNonInlineNormalFlow(
0245:                    FormatContext context, int parentWidth) {
0246:                //        Value al = CssLookup.getValue(getElement(), XhtmlCss.TEXT_ALIGN_INDEX);
0247:                CssValue cssAl = CssProvider.getEngineService()
0248:                        .getComputedValueForElement(getElement(),
0249:                                XhtmlCss.TEXT_ALIGN_INDEX);
0250:
0251:                //        if (al == CssValueConstants.RAVECENTER_VALUE) {
0252:                if (CssProvider.getValueService().isRaveCenterValue(cssAl)) {
0253:                    leftMargin = AUTO;
0254:                    rightMargin = AUTO;
0255:
0256:                    if (contentWidth == AUTO) {
0257:                        int availableWidth = parentWidth - 0 - 0 //leftMargin=0,left=0
0258:                                // Don't pre-subtracxt padding and borders since in the shrink to fit
0259:                                // calculation for tables I add these in. Revisit this. (See comments
0260:                                // under get preferred width calculation in the table.
0261:                                // -leftBorderWidth - leftPadding - rightPadding - rightBorderWidth
0262:                                - 0 - 0; // rightMargin=0, right=0;
0263:                        contentWidth = shrinkToFit(availableWidth, context);
0264:                        // For the table this will already include the borders and padding, since
0265:                        // the width property for tables are interpreted that way.
0266:                        // But that's not
0267:                        // how normal box computations work! Subtract them back out here.
0268:                        // TODO -- fix shrinkToFit so it doesn't do this for tables - and figure out
0269:                        // how that impacts nested tables and such.
0270:                        contentWidth -= (leftPadding + rightPadding
0271:                                + leftBorderWidth + rightBorderWidth);
0272:                    }
0273:                }
0274:
0275:                super .computeHorizNonInlineNormalFlow(context, parentWidth);
0276:            }
0277:
0278:            /**
0279:             * Implement the fixed table layout algorithm, described in the
0280:             * CSS2.1 spec:
0281:             * http://www.w3.org/TR/CSS21/tables.html#fixed-table-layout
0282:             */
0283:            private void fixedLayout(int[] columnWidths, FormatContext context) {
0284:                assert (rows > 0) && (columns > 0);
0285:
0286:                computeFixedColumnWidths(columnWidths, false);
0287:                formatCells(columnWidths, context);
0288:                positionCells(columnWidths, context);
0289:            }
0290:
0291:            private void computeFixedColumnWidths(int[] columnWidths,
0292:                    boolean scan) {
0293:                for (int i = 0; i < columns; i++) {
0294:                    columnWidths[i] = AUTO;
0295:                }
0296:
0297:                int tableWidth = getTableWidth(scan);
0298:
0299:                // Compute column widths:
0300:                // 1. "A column element with a value other than 'auto' for the
0301:                // 'width' property sets the width for that column."
0302:                // 2. "Otherwise, a cell in the first row with a value other
0303:                // than 'auto' for the 'width' property sets the width for that
0304:                // column. If the cell spans more than one column, the width is
0305:                // divided over the columns."
0306:                NodeList list = table.getChildNodes();
0307:                int len = list.getLength();
0308:                int currentColumn = 0;
0309:
0310:                for (int i = 0; i < len; i++) {
0311:                    Node child = (Node) list.item(i);
0312:
0313:                    if (child.getNodeType() != Node.ELEMENT_NODE) {
0314:                        continue;
0315:                    }
0316:
0317:                    Element element = (Element) child;
0318:                    //            Value display = CssLookup.getValue(element, XhtmlCss.DISPLAY_INDEX);
0319:                    CssValue cssDisplay = CssProvider.getEngineService()
0320:                            .getComputedValueForElement(element,
0321:                                    XhtmlCss.DISPLAY_INDEX);
0322:
0323:                    //            if (display == CssValueConstants.TABLE_COLUMN_GROUP_VALUE) {
0324:                    if (CssProvider.getValueService().isTableColumnGroupValue(
0325:                            cssDisplay)) {
0326:                        // XXX I shouldn't look for and process width attributes on
0327:                        // <colgroups> should I?
0328:                        NodeList list2 = element.getChildNodes();
0329:                        int len2 = list2.getLength();
0330:
0331:                        for (int j = 0; j < len2; j++) {
0332:                            Node child2 = (Node) list2.item(j);
0333:
0334:                            if (child2.getNodeType() != Node.ELEMENT_NODE) {
0335:                                continue;
0336:                            }
0337:
0338:                            Element element2 = (Element) child2;
0339:
0340:                            //                    if (CssLookup.getValue(element2, XhtmlCss.DISPLAY_INDEX) == CssValueConstants.TABLE_COLUMN_VALUE) {
0341:                            if (CssProvider
0342:                                    .getValueService()
0343:                                    .isTableColumnValue(
0344:                                            CssProvider
0345:                                                    .getEngineService()
0346:                                                    .getComputedValueForElement(
0347:                                                            element2,
0348:                                                            XhtmlCss.DISPLAY_INDEX))) {
0349:                                int colSpan = HtmlAttribute
0350:                                        .getIntegerAttributeValue(element2,
0351:                                                HtmlAttribute.SPAN, 1);
0352:
0353:                                if (colSpan <= 0) { // HTML 11.2.6: "0" means fill the row. TODO.
0354:                                    colSpan = 1;
0355:                                }
0356:
0357:                                computeFixedColumnWidth(columnWidths,
0358:                                        currentColumn, element2, colSpan,
0359:                                        tableWidth);
0360:                                currentColumn += colSpan;
0361:                            }
0362:                        }
0363:                        //            } else if (display == CssValueConstants.TABLE_COLUMN_VALUE) {
0364:                    } else if (CssProvider.getValueService()
0365:                            .isTableColumnValue(cssDisplay)) {
0366:                        int colSpan = HtmlAttribute.getIntegerAttributeValue(
0367:                                element, HtmlAttribute.SPAN, 1);
0368:
0369:                        if (colSpan <= 0) { // HTML 11.2.6: "0" means fill the row. TODO.
0370:                            colSpan = 1;
0371:                        }
0372:
0373:                        computeFixedColumnWidth(columnWidths, currentColumn,
0374:                                element, colSpan, tableWidth);
0375:                        currentColumn += colSpan;
0376:                    }
0377:                }
0378:
0379:                boolean needMoreWidths = false;
0380:
0381:                for (int i = 0; i < columns; i++) {
0382:                    if (columnWidths[i] == AUTO) {
0383:                        needMoreWidths = true;
0384:
0385:                        break;
0386:                    }
0387:                }
0388:
0389:                if (needMoreWidths) {
0390:                    // I didn't get all the widths I needed from the col elements.
0391:                    // So look at the first row too.
0392:                    boolean seenFirstRow = false;
0393:
0394:                    for (int i = 0; i < len; i++) {
0395:                        if (seenFirstRow) {
0396:                            break;
0397:                        }
0398:
0399:                        Node trn = (Node) list.item(i);
0400:
0401:                        if (trn.getNodeType() != Node.ELEMENT_NODE) {
0402:                            continue;
0403:                        }
0404:
0405:                        Element tr = (Element) trn;
0406:                        //                Value display = CssLookup.getValue(tr, XhtmlCss.DISPLAY_INDEX);
0407:                        CssValue cssDisplay = CssProvider.getEngineService()
0408:                                .getComputedValueForElement(tr,
0409:                                        XhtmlCss.DISPLAY_INDEX);
0410:
0411:                        //                if (display == CssValueConstants.TABLE_ROW_VALUE) {
0412:                        if (CssProvider.getValueService().isTableRowValue(
0413:                                cssDisplay)) {
0414:                            computeFixedColumnWidth(columnWidths, tr,
0415:                                    tableWidth);
0416:
0417:                            break;
0418:                            //                } else if ((display == CssValueConstants.TABLE_ROW_GROUP_VALUE) ||
0419:                            //                        (display == CssValueConstants.TABLE_HEADER_GROUP_VALUE) ||
0420:                            //                        (display == CssValueConstants.TABLE_FOOTER_GROUP_VALUE)) {
0421:                        } else if (CssProvider.getValueService()
0422:                                .isTableRowGroupValue(cssDisplay)
0423:                                || CssProvider.getValueService()
0424:                                        .isTableHeaderGroupValue(cssDisplay)
0425:                                || CssProvider.getValueService()
0426:                                        .isTableFooterGroupValue(cssDisplay)) {
0427:                            NodeList list2 = tr.getChildNodes();
0428:                            int len2 = list2.getLength();
0429:
0430:                            for (int j = 0; j < len2; j++) {
0431:                                Node trn2 = (Node) list2.item(j);
0432:
0433:                                if (trn2.getNodeType() != Node.ELEMENT_NODE) {
0434:                                    continue;
0435:                                }
0436:
0437:                                Element tr2 = (Element) trn2;
0438:
0439:                                //                        if (CssLookup.getValue(tr2, XhtmlCss.DISPLAY_INDEX) == CssValueConstants.TABLE_ROW_VALUE) {
0440:                                if (CssProvider
0441:                                        .getValueService()
0442:                                        .isTableRowValue(
0443:                                                CssProvider
0444:                                                        .getEngineService()
0445:                                                        .getComputedValueForElement(
0446:                                                                tr2,
0447:                                                                XhtmlCss.DISPLAY_INDEX))) {
0448:                                    computeFixedColumnWidth(columnWidths, tr2,
0449:                                            tableWidth);
0450:                                    seenFirstRow = true;
0451:
0452:                                    break;
0453:                                }
0454:                            }
0455:                        }
0456:                    }
0457:                }
0458:
0459:                int numRemaining = countUnassignedColumns(columnWidths);
0460:
0461:                if (numRemaining > 0) {
0462:                    // 3. Any remaining columns equally divide the remaining
0463:                    // horizontal table space (minus borders or cell spacing).
0464:                    int portion = tableWidth / numRemaining;
0465:                    int remainder = tableWidth % numRemaining;
0466:                    int nextPos = 0;
0467:
0468:                    for (int k = 0; k < numRemaining; k++) {
0469:                        // Find next unassigned column
0470:                        while (columnWidths[nextPos] != AUTO) {
0471:                            nextPos++;
0472:                        }
0473:
0474:                        columnWidths[nextPos] = portion;
0475:
0476:                        if (k == 0) {
0477:                            // The first column also gets the
0478:                            // remainder
0479:                            columnWidths[nextPos] += remainder;
0480:                        }
0481:
0482:                        nextPos++;
0483:                    }
0484:                }
0485:
0486:                // From CSS21 17.5.2:
0487:                //     The width of the table is then the greater of the value
0488:                //     of the 'width' property for the table element and the sum
0489:                //     of the column widths (plus cell spacing or borders). If
0490:                //     the table is wider than the columns, the extra space
0491:                //     should be distributed over the columns.
0492:                // Should we distribute proportionally or assign equal amounts to
0493:                // each column? Mozilla seems to do it proportionally so lets do that.
0494:                int totalWidth = 0;
0495:
0496:                for (int i = 0; i < columns; i++) {
0497:                    totalWidth += columnWidths[i];
0498:                }
0499:
0500:                if (totalWidth >= tableWidth) {
0501:                    tableWidth = totalWidth;
0502:                } else {
0503:                    // Extra space - distribute.
0504:                    int leftOver = tableWidth - totalWidth;
0505:                    int assigned = 0;
0506:
0507:                    for (int i = 0; i < columns; i++) {
0508:                        int portion = (columnWidths[i] * leftOver) / totalWidth;
0509:                        columnWidths[i] += portion;
0510:                        assigned += portion;
0511:                    }
0512:
0513:                    columnWidths[0] += (leftOver - assigned); // remainder from rounding errors
0514:                }
0515:            }
0516:
0517:            /** Set the position, width and height and alignment for all the cells of the table,
0518:             * and set the total width/height of the table itself based on the
0519:             * cumulative size of the rows and columns.
0520:             */
0521:            private void positionCells(int[] columnWidths, FormatContext context) {
0522:                /** Special meaning: positive numbers: fixed width. Negative
0523:                 * numbers: negative percentage. AUTO: unconstrained
0524:                 * (default) */
0525:                int[] constraints = null;
0526:                int desiredHeight = getTableHeight();
0527:
0528:                if (desiredHeight == AUTO) {
0529:                    // If there is no table height set, don't compoute
0530:                    // constraints since we won't need them.
0531:                    desiredHeight = 0;
0532:                } else {
0533:                    constraints = new int[rows];
0534:                }
0535:
0536:                rowHeights = new int[rows];
0537:
0538:                for (int i = 0; i < rows; i++) {
0539:                    int rowHeight = 0;
0540:
0541:                    if (rowElements[i] != null) {
0542:                        //                Value value = CssLookup.getValue(rowElements[i], XhtmlCss.HEIGHT_INDEX);
0543:                        CssValue cssValue = CssProvider.getEngineService()
0544:                                .getComputedValueForElement(rowElements[i],
0545:                                        XhtmlCss.HEIGHT_INDEX);
0546:
0547:                        //                if (value == CssValueConstants.AUTO_VALUE) {
0548:                        if (CssProvider.getValueService().isAutoValue(cssValue)) {
0549:                            rowHeight = 0;
0550:
0551:                            if (constraints != null) {
0552:                                // AUTO means use minimum necessary value
0553:                                constraints[i] = AUTO;
0554:                            }
0555:
0556:                            // constraints left at AUTO, but don't set since we
0557:                            // don't initialize constraints unless we have to
0558:                        } else {
0559:                            //                    boolean wasPercentage =
0560:                            //                        value instanceof ComputedValue &&
0561:                            //                        (((ComputedValue)value).getCascadedValue().getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE);
0562:                            boolean wasPercentage = cssValue instanceof  CssComputedValue
0563:                                    && CssProvider
0564:                                            .getValueService()
0565:                                            .isOfPrimitivePercentageType(
0566:                                                    ((CssComputedValue) cssValue)
0567:                                                            .getCascadedValue());
0568:
0569:                            if (wasPercentage) {
0570:                                rowHeight = 0;
0571:
0572:                                //                        int percentage =
0573:                                //                            (int)((ComputedValue)value).getCascadedValue().getFloatValue();
0574:                                int percentage = (int) ((CssComputedValue) cssValue)
0575:                                        .getCascadedValue().getFloatValue();
0576:
0577:                                if (percentage < 0) {
0578:                                    percentage = 0;
0579:                                }
0580:
0581:                                if (constraints != null) {
0582:                                    // negative numbers indicates percentage
0583:                                    constraints[i] = -percentage;
0584:                                }
0585:                            } else {
0586:                                //                        rowHeight = (int)value.getFloatValue();
0587:                                rowHeight = (int) cssValue.getFloatValue();
0588:
0589:                                if (rowHeight < 0) {
0590:                                    rowHeight = 0;
0591:                                }
0592:
0593:                                if (constraints != null) {
0594:                                    // positive numbers indicates actual length
0595:                                    constraints[i] = rowHeight;
0596:                                }
0597:                            }
0598:                        }
0599:                    }
0600:
0601:                    for (int j = 0; j < columns; j++) {
0602:                        CssBox box = cells[i][j];
0603:
0604:                        if ((box == null) || (box == OCCUPIED)) {
0605:                            continue;
0606:                        }
0607:
0608:                        int rowspan = rowspans[i][j];
0609:
0610:                        if (rowspan == 1) {
0611:                            if (box.getHeight() > rowHeight) {
0612:                                rowHeight = box.getHeight();
0613:                            }
0614:                        } // else: we take care of multi-row cells in a second pass
0615:
0616:                        // when we have all the individual-cell heights; we then
0617:                        // distribute the minimum height required by the cell
0618:                        // over the rows, if necessary.
0619:                        int colspan = colspans[i][j];
0620:                        int w = 0;
0621:
0622:                        for (int m = 0; m < colspan; m++) {
0623:                            w += columnWidths[j + m];
0624:                        }
0625:
0626:                        box.width = w - cellSpacing;
0627:                        box.contentWidth = box.width
0628:                                - (box.leftBorderWidth + box.leftPadding
0629:                                        + box.rightPadding + box.rightBorderWidth);
0630:                    }
0631:
0632:                    rowHeights[i] = rowHeight;
0633:                }
0634:
0635:                // Account for tall cells that span multiple rows: here we need
0636:                // to make sure that the sum of the rows spanning the cell is
0637:                // as large as the cell height
0638:                for (int i = 0; i < rows; i++) {
0639:                    for (int j = 0; j < columns; j++) {
0640:                        CssBox box = cells[i][j];
0641:
0642:                        if ((box == null) || (box == OCCUPIED)) {
0643:                            continue;
0644:                        }
0645:
0646:                        int rowspan = rowspans[i][j];
0647:
0648:                        if (rowspan > 1) {
0649:                            int rowsum = 0;
0650:
0651:                            for (int m = 0; m < rowspan; m++) {
0652:                                rowsum += rowHeights[i + m];
0653:                            }
0654:
0655:                            if (box.getHeight() > rowsum) {
0656:                                // Distribute extra height proportionally over the
0657:                                // existing rows. Could probably use a better
0658:                                // algorithm than that, but... revisit.
0659:                                // 
0660:                                int leftOver = box.getHeight() - rowsum;
0661:                                int assigned = 0;
0662:
0663:                                for (int m = 0; m < rowspan; m++) {
0664:                                    // XXX #91134 Possible division by zero.
0665:                                    //                            int portion = (rowHeights[i + m] * leftOver) / rowsum;
0666:                                    int portion = rowsum == 0 ? 0
0667:                                            : (rowHeights[i + m] * leftOver)
0668:                                                    / rowsum;
0669:
0670:                                    rowHeights[i + m] += portion;
0671:                                    assigned += portion;
0672:                                }
0673:
0674:                                rowHeights[i] += (leftOver - assigned); // remainder from rounding errors
0675:                            }
0676:                        }
0677:                    }
0678:                }
0679:
0680:                //contentHeight = y-topBorderWidth-topPadding;
0681:                contentHeight = 0;
0682:
0683:                for (int m = 0; m < rows; m++) {
0684:                    contentHeight += rowHeights[m];
0685:                }
0686:
0687:                // Now let's see if we should adjust the height upwards in case
0688:                // the user has set a larger height Note that we DON'T shrink
0689:                // the table. According to CSS2.1 section 17.5.3 the behavior
0690:                // here is undefined.
0691:                if (desiredHeight > contentHeight) {
0692:                    // Distribute the available space. First give space to the
0693:                    // percentage rows. Then assign the rest proportionally
0694:                    // to the remaining UNCONSTRAINED rows. Note that there
0695:                    // may be no such columns.  If so we will ignore the
0696:                    // requested height on the table.
0697:                    int leftOver = desiredHeight - contentHeight;
0698:                    int assigned = 0;
0699:
0700:                    // TODO - Mozilla seems to leave header cells alone, it does not
0701:                    // grow these at all....
0702:                    for (int i = 0; i < rows; i++) {
0703:                        if (constraints[i] < 0) {
0704:                            int percent = -constraints[i];
0705:                            int portion = (percent * leftOver) / 100;
0706:                            rowHeights[i] += portion;
0707:                            assigned += portion;
0708:
0709:                            if (assigned > leftOver) {
0710:                                rowHeights[i] -= (assigned - leftOver);
0711:                                assigned = leftOver;
0712:
0713:                                break;
0714:                            }
0715:                        }
0716:                    }
0717:
0718:                    if (assigned < leftOver) {
0719:                        leftOver = leftOver - assigned;
0720:                        assigned = 0;
0721:
0722:                        // Spread the remainder over the unconstrained
0723:                        int unconstrained = 0;
0724:
0725:                        for (int i = 0; i < rows; i++) {
0726:                            if (constraints[i] == AUTO) {
0727:                                unconstrained += rowHeights[i];
0728:                            }
0729:                        }
0730:
0731:                        if (unconstrained > 0) {
0732:                            int lastCol = -1;
0733:
0734:                            for (int i = 0; i < rows; i++) {
0735:                                if (constraints[i] == AUTO) {
0736:                                    int portion = (rowHeights[i] * leftOver)
0737:                                            / unconstrained;
0738:                                    rowHeights[i] += portion;
0739:                                    assigned += portion;
0740:                                    lastCol = i;
0741:                                }
0742:                            }
0743:
0744:                            // Remainder from potential rounding errors
0745:                            rowHeights[lastCol] += (leftOver - assigned);
0746:                        } else {
0747:                            // There are no unconstrained columns. Hand out the
0748:                            // rest to the percentages, if any
0749:                            int percentageSum = 0;
0750:
0751:                            for (int i = 0; i < rows; i++) {
0752:                                if (constraints[i] < 0) {
0753:                                    int percent = -constraints[i];
0754:                                    percentageSum += percent;
0755:                                }
0756:                            }
0757:
0758:                            if (percentageSum > 0) {
0759:                                int lastCol = -1;
0760:
0761:                                for (int i = 0; i < rows; i++) {
0762:                                    if (constraints[i] < 0) {
0763:                                        int percent = -constraints[i];
0764:                                        int portion = (percent * leftOver)
0765:                                                / percentageSum;
0766:                                        rowHeights[i] += portion;
0767:                                        assigned += portion;
0768:                                        lastCol = i;
0769:                                    }
0770:                                }
0771:
0772:                                // Remainder from potential rounding errors
0773:                                rowHeights[lastCol] += (leftOver - assigned);
0774:                            }
0775:                            //                    else {
0776:                            //                        // You have a fully constrained table with
0777:                            //                        // assigned heights -- height cannot be
0778:                            //                        // accomodated. Use table computed height.
0779:                            //                    }
0780:                        }
0781:                    }
0782:
0783:                    // Set contentHeight. I -might- be able to just do
0784:                    // contentHeight = desiredHeight but I need to check border
0785:                    // accounting etc.
0786:                    contentHeight = 0;
0787:
0788:                    for (int m = 0; m < rows; m++) {
0789:                        contentHeight += rowHeights[m];
0790:                    }
0791:                }
0792:
0793:                // We've already accounted for the cellspacing above all the cells
0794:                // (as part of the cell bounds) but we need spacing below the bottom-most
0795:                // row as well
0796:                contentHeight += cellSpacing;
0797:                super .height = topBorderWidth + topPadding + contentHeight
0798:                        + bottomPadding + bottomBorderWidth;
0799:                contentWidth = 0;
0800:
0801:                for (int m = 0; m < columns; m++) {
0802:                    contentWidth += columnWidths[m];
0803:                }
0804:
0805:                contentWidth += cellSpacing; // spacing behind rightmost column
0806:                super .width = leftBorderWidth + leftPadding + contentWidth
0807:                        + rightPadding + rightBorderWidth;
0808:
0809:                // Assign box height
0810:                // Make another pass over the table and assign
0811:                // heights to all the cells. We couldn't do that
0812:                // in the first pass since we hadn't looked up the
0813:                // total row height for the WHOLE table yet - and
0814:                // we need all the row heights so that we can compute
0815:                // cell heights for cells that have rowspans referring
0816:                // to rows below the current row.
0817:                for (int i = 0; i < rows; i++) {
0818:                    for (int j = 0; j < columns; j++) {
0819:                        CssBox box = cells[i][j];
0820:
0821:                        if ((box == null) || (box == OCCUPIED)) {
0822:                            continue;
0823:                        }
0824:
0825:                        int rowspan = rowspans[i][j];
0826:                        int h = 0;
0827:
0828:                        for (int m = 0; m < rowspan; m++) {
0829:                            h += rowHeights[i + m];
0830:                        }
0831:
0832:                        //box.contentHeight = h;
0833:                        //box.height = box.topBorderWidth+box.topPadding+box.contentHeight+box.bottomPadding+box.bottomBorderWidth;
0834:                        box.height = h - cellSpacing;
0835:                        box.contentHeight = box.height
0836:                                - (box.topBorderWidth + box.topPadding
0837:                                        + box.bottomPadding + box.bottomBorderWidth);
0838:                    }
0839:                }
0840:
0841:                formatCaption(context, true, super .width, super .height);
0842:
0843:                // Assign positions
0844:                int y = tableTop + topBorderWidth + topPadding + cellSpacing;
0845:
0846:                for (int i = 0; i < rows; i++) {
0847:                    int x = leftBorderWidth + leftPadding + cellSpacing
0848:                            + tableLeft;
0849:
0850:                    for (int j = 0; j < columns; j++) {
0851:                        CellBox box = cells[i][j];
0852:
0853:                        if ((box != null) && (box != OCCUPIED)) {
0854:                            box.setLocation(x, y);
0855:
0856:                            // I can't move the box itself to align, since the
0857:                            // box itself paints backgrounds, cell borders, etc.
0858:                            // The box -contents- have to be shifted instead.
0859:                            box.align();
0860:                        }
0861:
0862:                        x += columnWidths[j];
0863:                    }
0864:
0865:                    y += rowHeights[i];
0866:                }
0867:
0868:                formatCaption(context, false, super .width, super .height);
0869:            }
0870:
0871:            /** Format the optional caption. Called both above and below the table such that
0872:             * the table can react accordingly. This method will update variables like the
0873:             * width and height settings of the box, the tableTop/tableLeft/tableRight/tableBottom
0874:             * fields, etc., when applicable.
0875:             */
0876:            private void formatCaption(FormatContext context, boolean above,
0877:                    int width, int height) {
0878:                if (captionBoxes == null) {
0879:                    return;
0880:                }
0881:
0882:                if (above && captionAbove) {
0883:                    CssBox prevBox = null;
0884:
0885:                    int x = 0;
0886:                    int y = 0;
0887:
0888:                    for (int i = 0, n = captionBoxes.size(); i < n; i++) {
0889:                        CssBox box = captionBoxes.get(i);
0890:
0891:                        if (box.getBoxType().isNormalFlow()) {
0892:                            prevBox = box;
0893:                        }
0894:
0895:                        // Should be a ContainerBox, for the caption element
0896:                        box.setX(x);
0897:                        box.setY(y);
0898:
0899:                        // The containing block as obtained in layoutChild's call to setContainingBlock
0900:                        // will be asking for the contentWidth of its parent, the table, so set that
0901:                        // here...
0902:                        int oldContentWidth = contentWidth;
0903:                        contentWidth = width;
0904:
0905:                        try {
0906:                            layoutChild(box, context, true);
0907:                        } finally {
0908:                            contentWidth = oldContentWidth;
0909:                        }
0910:
0911:                        super .height += box.height;
0912:                        super .contentHeight += box.height;
0913:                        y += box.height;
0914:
0915:                        if (box.width > this .width) {
0916:                            int diff = box.width - this .width;
0917:                            contentWidth += diff;
0918:                            this .width = box.width;
0919:
0920:                            // Firefox does not center the table when the caption is too wide
0921:                            // so let's do the same
0922:                            //int extra = (this.width-width)/2;
0923:                            //tableRight = extra;
0924:                            //tableLeft = extra;
0925:                            // So instead just put more room on the right
0926:                            tableRight = this .width - width;
0927:                        }
0928:                    }
0929:
0930:                    // Compute collapse between caption and table, and from there, the
0931:                    // table y position
0932:                    if (prevBox != null) {
0933:                        int margin;
0934:
0935:                        int prevMargin = prevBox.getCollapsedBottomMargin();
0936:                        int boxMargin = getCollapsedTopMargin(); // the table
0937:
0938:                        if ((prevMargin >= 0) && (boxMargin >= 0)) {
0939:                            // Normal case
0940:                            //  The larger of adjacent margin values is used.
0941:                            margin = Math.max(prevMargin, boxMargin);
0942:
0943:                            // OLD:
0944:                        } else if ((prevMargin < 0) && (boxMargin < 0)) {
0945:                            // If the adjacent margins are all negative, the larger
0946:                            // of the negative values is used.
0947:                            // XXX this is not how I re-read the spec; it says "If
0948:                            // there are no positive margins, the absolute maximum
0949:                            // of the negative adjoining margins is deducted from
0950:                            // zero."   So I take abs
0951:                            //margin = Math.max(-prevMargin, -boxMargin);
0952:                            margin = Math.min(prevMargin, boxMargin);
0953:                        } else {
0954:                            // If positive and negative vertical margins are
0955:                            // adjacent, the value should be collapsed thus: the
0956:                            // largest of the negative margin values should be
0957:                            // subtracted from the largest positive margin value.
0958:                            if ((prevMargin >= 0) && (boxMargin < 0)) {
0959:                                margin = prevMargin + boxMargin;
0960:                            } else {
0961:                                assert (prevMargin < 0) && (boxMargin >= 0);
0962:                                margin = boxMargin + prevMargin;
0963:                            }
0964:                        }
0965:
0966:                        tableTop = prevBox.getY() + prevBox.getHeight();
0967:                        effectiveTopMargin = margin;
0968:                        prevBox.effectiveTopMargin = 0;
0969:                    }
0970:
0971:                    tableRight = tableLeft + width;
0972:                    tableBottom = tableTop + height;
0973:                } else if (!above && !captionAbove) {
0974:                    tableTop = 0;
0975:                    tableLeft = 0;
0976:                    tableBottom = this .height;
0977:
0978:                    int x = 0;
0979:                    int y = this .height;
0980:
0981:                    for (int i = 0, n = captionBoxes.size(); i < n; i++) {
0982:                        CssBox box = captionBoxes.get(i);
0983:
0984:                        // Should be a ContainerBox, for the caption element
0985:                        box.setX(x);
0986:                        box.setY(y);
0987:
0988:                        int oldContentWidth = contentWidth;
0989:                        contentWidth = width; // See related comment in captionAbove section
0990:
0991:                        try {
0992:                            layoutChild(box, context, true);
0993:                        } finally {
0994:                            contentWidth = oldContentWidth;
0995:                        }
0996:
0997:                        super .height += box.height;
0998:                        super .contentHeight += box.height;
0999:                        y += box.height;
1000:
1001:                        if (box.width > this .width) {
1002:                            int diff = box.width - this .width;
1003:                            contentWidth += diff;
1004:                            this .width = box.width;
1005:
1006:                            // XXX Can't do this here, it's too late; I've already positioned all
1007:                            // the table cells....
1008:                            //int extra = (this.width-width)/2;
1009:                            //tableRight = extra;
1010:                            //tableLeft = extra;
1011:                            // So instead just put more room on the right
1012:                            tableRight = this .width - width;
1013:                        }
1014:                    }
1015:
1016:                    tableRight = tableLeft + width;
1017:                }
1018:            }
1019:
1020:            /** Count how many widths in the columnWidths array are still
1021:             * unassigned.
1022:             */
1023:            private int countUnassignedColumns(int[] columnWidths) {
1024:                int numRemaining = 0;
1025:
1026:                for (int i = 0; i < columns; i++) {
1027:                    if (columnWidths[i] == AUTO) {
1028:                        numRemaining++;
1029:                    }
1030:                }
1031:
1032:                return numRemaining;
1033:            }
1034:
1035:            /** Initialize the column widths for the given element (may be
1036:             * a &lt;col&gt; or a &lt;td&gt; or a &lt;th&gt;), following
1037:             * the fixed table layout algorithm.
1038:             */
1039:            private void computeFixedColumnWidth(int[] columnWidths,
1040:                    int column, Element element, int colSpan, int parentWidth) {
1041:                // Compute the width of the column. We can't just use the
1042:                // default getLength behavior on the element, since we want
1043:                // percentages to be relative to the parent width, which is not
1044:                // the same thing as the containing block width (because for tables,
1045:                // we subtract border widths etc. from the width unlike normal
1046:                // box behavior. E.g. a table with "width: 100%" and "border-width: 10"
1047:                // will exactly fill the containing block, rather than be 20 pixels
1048:                // wider than it if you had set the same properties on a div.
1049:                int w;
1050:                //        Value value = CssLookup.getValue(element, XhtmlCss.WIDTH_INDEX);
1051:                CssValue cssValue = CssProvider.getEngineService()
1052:                        .getComputedValueForElement(element,
1053:                                XhtmlCss.WIDTH_INDEX);
1054:
1055:                //        if (value == CssValueConstants.AUTO_VALUE) {
1056:                if (CssProvider.getValueService().isAutoValue(cssValue)) {
1057:                    w = AUTO;
1058:                } else {
1059:                    //            boolean wasPercentage =
1060:                    //                value instanceof ComputedValue &&
1061:                    //                (((ComputedValue)value).getCascadedValue().getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE);
1062:                    boolean wasPercentage = cssValue instanceof  CssComputedValue
1063:                            && CssProvider.getValueService()
1064:                                    .isOfPrimitivePercentageType(
1065:                                            ((CssComputedValue) cssValue)
1066:                                                    .getCascadedValue());
1067:
1068:                    if (wasPercentage) {
1069:                        //                w = ((int)((ComputedValue)value).getCascadedValue().getFloatValue() * parentWidth) / 100;
1070:                        w = ((int) ((CssComputedValue) cssValue)
1071:                                .getCascadedValue().getFloatValue() * parentWidth) / 100;
1072:                    } else {
1073:                        //                w = (int)value.getFloatValue();
1074:                        w = (int) cssValue.getFloatValue();
1075:                    }
1076:                }
1077:
1078:                if (w != AUTO) {
1079:                    if (colSpan > 1) {
1080:                        // Divide up between the columns
1081:                        int portion = w / colSpan;
1082:
1083:                        for (int k = 0; (k < colSpan)
1084:                                && ((column + k) < columns); k++) {
1085:                            columnWidths[column + k] = portion;
1086:
1087:                            if (k == 0) {
1088:                                // The first column also gets the
1089:                                // remainder
1090:                                columnWidths[column] += (w % colSpan);
1091:                            }
1092:                        }
1093:                    } else {
1094:                        columnWidths[column] = w;
1095:                    }
1096:                }
1097:            }
1098:
1099:            /** Initialize the column widths for the given row following
1100:             * the fixed table layout algorithm.
1101:             */
1102:            private void computeFixedColumnWidth(int[] columnWidths,
1103:                    Element tr, int parentWidth) {
1104:                NodeList list2 = tr.getChildNodes();
1105:                int len2 = list2.getLength();
1106:                int column = 0;
1107:
1108:                for (int j = 0; j < len2; j++) {
1109:                    Node tdthn = (Node) list2.item(j);
1110:
1111:                    if (tdthn.getNodeType() != Node.ELEMENT_NODE) {
1112:                        continue;
1113:                    }
1114:
1115:                    Element tdth = (Element) tdthn;
1116:                    //            Value display = CssLookup.getValue(tdth, XhtmlCss.DISPLAY_INDEX);
1117:                    CssValue cssDisplay = CssProvider.getEngineService()
1118:                            .getComputedValueForElement(tdth,
1119:                                    XhtmlCss.DISPLAY_INDEX);
1120:
1121:                    //            if (display != CssValueConstants.TABLE_CELL_VALUE) {
1122:                    if (CssProvider.getValueService().isTableCellValue(
1123:                            cssDisplay)) {
1124:                        continue;
1125:                    }
1126:
1127:                    int colSpan = HtmlAttribute.getIntegerAttributeValue(tdth,
1128:                            HtmlAttribute.COLSPAN, 1);
1129:
1130:                    if (colSpan <= 0) { // HTML 11.2.6: "0" means fill the row. TODO.
1131:                        colSpan = 1;
1132:                    }
1133:
1134:                    computeFixedColumnWidth(columnWidths, column, tdth,
1135:                            colSpan, parentWidth);
1136:                    column += colSpan;
1137:                }
1138:            }
1139:
1140:            protected void initializeBorder() {
1141:                int defStyle = (borderWidth == 0) ? CssBorder.STYLE_NONE
1142:                        : CssBorder.STYLE_OUTSET;
1143:                border = CssBorder.getBorder(getElement(), borderWidth,
1144:                        defStyle, frame);
1145:
1146:                if (border != null) {
1147:                    leftBorderWidth = border.getLeftBorderWidth();
1148:                    topBorderWidth = border.getTopBorderWidth();
1149:                    bottomBorderWidth = border.getBottomBorderWidth();
1150:                    rightBorderWidth = border.getRightBorderWidth();
1151:                }
1152:
1153:                considerDesignBorder();
1154:            }
1155:
1156:            protected void createChildren(CreateContext context) {
1157:                // Layout Caption - top or bottom? 
1158:                Element caption = findCaption(getElement());
1159:
1160:                captionAbove = true;
1161:
1162:                if (caption != null) {
1163:                    //            Value side = CssLookup.getValue(caption, XhtmlCss.CAPTION_SIDE_INDEX);
1164:                    CssValue cssSide = CssProvider.getEngineService()
1165:                            .getComputedValueForElement(caption,
1166:                                    XhtmlCss.CAPTION_SIDE_INDEX);
1167:
1168:                    //            if (side == CssValueConstants.BOTTOM_VALUE) {
1169:                    if (CssProvider.getValueService().isBottomValue(cssSide)) {
1170:                        captionAbove = false;
1171:                    }
1172:
1173:                    finishLineBox(context);
1174:
1175:                    if (captionAbove) {
1176:                        addNode(context, caption, null, null, null);
1177:
1178:                        int n = getBoxCount();
1179:                        captionBoxes = new ArrayList<CssBox>(n);
1180:
1181:                        for (int i = 0; i < n; i++) {
1182:                            captionBoxes.add(getBox(i));
1183:                        }
1184:                    }
1185:                }
1186:
1187:                // Calculate the number of columns in the table
1188:                // See section 19.2.1 in the XHTML Tables Module
1189:                // (http://www.w3.org/TR/xhtml2/mod-tables.html)
1190:                // (The number of rows = number of <tr> children of the <table>
1191:                // element)
1192:                columns = computeColumnCount(table);
1193:                rows = computeRowCount(table);
1194:
1195:                // TODO - these may have percents, which I'll totally botch
1196:                // here - gotta look for that!
1197:                // XXX Shouldn't I initialize these in TableBox.initialize(),
1198:                // not here?
1199:                cellSpacing = HtmlAttribute.getIntegerAttributeValue(table,
1200:                        HtmlAttribute.CELLSPACING, 2);
1201:
1202:                // If cell spacing has not been set, initialize it using
1203:                // the table margin? Or take the max again between cellspacing
1204:                // and table margins, the way we take the max between an individual
1205:                // td's padding and the cellpadding attribute?
1206:                cellPadding = HtmlAttribute.getIntegerAttributeValue(table,
1207:                        HtmlAttribute.CELLPADDING, 1);
1208:
1209:                // Frame attribute support
1210:                int defaultBorderWidth = 0;
1211:
1212:                if (table.hasAttribute(HtmlAttribute.FRAME)) {
1213:                    String attr = table.getAttribute(HtmlAttribute.FRAME);
1214:
1215:                    if (attr.equals("above")) { // NOI18N
1216:                        frame = CssBorder.FRAME_TOP;
1217:                    } else if (attr.equals("below")) { // NOI18N
1218:                        frame = CssBorder.FRAME_BOTTOM;
1219:                    } else if (attr.equals("hsides")) { // NOI18N
1220:                        frame = CssBorder.FRAME_TOP | CssBorder.FRAME_BOTTOM;
1221:                    } else if (attr.equals("vsides")) { // NOI18N
1222:                        frame = CssBorder.FRAME_LEFT | CssBorder.FRAME_RIGHT;
1223:                    } else if (attr.equals("lhs")) { // NOI18N
1224:                        frame = CssBorder.FRAME_LEFT;
1225:                    } else if (attr.equals("rhs")) { // NOI18N
1226:                        frame = CssBorder.FRAME_RIGHT;
1227:                    } else if (attr.equals("void")) { // NOI18N
1228:                        frame = 0;
1229:                    } else if ((attr.length() == 0) || attr.equals("box") || // NOI18N
1230:                            attr.equals("border")) { // NOI18N
1231:                        frame = CssBorder.FRAME_BOX;
1232:                    }
1233:
1234:                    defaultBorderWidth = (frame != 0) ? CssBorder.WIDTH_THIN
1235:                            : 0;
1236:                }
1237:
1238:                // Border: percentages are not allowed
1239:                borderWidth = HtmlAttribute.getIntegerAttributeValue(table,
1240:                        HtmlAttribute.BORDER, defaultBorderWidth);
1241:
1242:                if (borderWidth < 0) {
1243:                    borderWidth = 0;
1244:                }
1245:
1246:                if (frame == 0) {
1247:                    borderWidth = 0;
1248:                }
1249:
1250:                //        MarkupDesignBean bean = getDesignBean();
1251:                //        MarkupDesignBean bean = CssBox.getMarkupDesignBeanForCssBox(this);
1252:                //        if (bean != null) {
1253:                //            DesignInfo info = bean.getDesignInfo();
1254:                //
1255:                //            if (info instanceof MarkupTableDesignInfo) {
1256:                //                tableDesignInfo = (MarkupTableDesignInfo)info;
1257:                //            }
1258:                //        }
1259:
1260:                // TODO - deal with the table header, if one is specified.
1261:                // TODO Group the columns according to any column group specifications.
1262:                // Render the cells, row by row and grouped in appropriate columns
1263:                // Render the table footer, if one is specified
1264:                fixedLayout = false;
1265:
1266:                // A value of "auto" for width implies autoLayout, so only
1267:                // consult the table-layout css property if it's non-auto.
1268:                if (contentWidth != AUTO) {
1269:                    //            Value layout = CssLookup.getValue(table, XhtmlCss.TABLE_LAYOUT_INDEX);
1270:                    CssValue cssLayout = CssProvider.getEngineService()
1271:                            .getComputedValueForElement(table,
1272:                                    XhtmlCss.TABLE_LAYOUT_INDEX);
1273:
1274:                    //            if (layout == CssValueConstants.FIXED_VALUE) {
1275:                    if (CssProvider.getValueService().isFixedValue(cssLayout)) {
1276:                        fixedLayout = true;
1277:                    }
1278:                }
1279:
1280:                //        Value collapse = CssLookup.getValue(table, XhtmlCss.BORDER_COLLAPSE_INDEX);
1281:                CssValue cssCollapse = CssProvider.getEngineService()
1282:                        .getComputedValueForElement(table,
1283:                                XhtmlCss.BORDER_COLLAPSE_INDEX);
1284:
1285:                if (table.hasAttribute(HtmlAttribute.RULES)) {
1286:                    String attr = table.getAttribute(HtmlAttribute.RULES);
1287:
1288:                    if (attr.equals("groups")) { // NOI18N
1289:
1290:                        // XXX not yet supported
1291:                        //collapse = CssValueConstants.COLLAPSE_VALUE;
1292:                        rules = CssBorder.FRAME_BOX;
1293:                        //                collapse = CssValueConstants.COLLAPSE_VALUE;
1294:                    } else if (attr.equals("rows")) { // NOI18N
1295:                        rules = CssBorder.FRAME_TOP | CssBorder.FRAME_BOTTOM;
1296:                        //                collapse = CssValueConstants.COLLAPSE_VALUE;
1297:                        cssCollapse = CssProvider.getValueService()
1298:                                .getCollapseCssValueConstant();
1299:                    } else if (attr.equals("cols")) { // NOI18N
1300:                        rules = CssBorder.FRAME_LEFT | CssBorder.FRAME_RIGHT;
1301:                        //                collapse = CssValueConstants.COLLAPSE_VALUE;
1302:                        cssCollapse = CssProvider.getValueService()
1303:                                .getCollapseCssValueConstant();
1304:                    } else if (attr.equals("all")) { // NOI18N
1305:                        rules = CssBorder.FRAME_BOX;
1306:                        //                collapse = CssValueConstants.COLLAPSE_VALUE;
1307:                        cssCollapse = CssProvider.getValueService()
1308:                                .getCollapseCssValueConstant();
1309:                    } // else: none -- FRAME_UNSET
1310:                }
1311:
1312:                //        if (collapse == CssValueConstants.COLLAPSE_VALUE) {
1313:                if (CssProvider.getValueService().isCollapseValue(cssCollapse)) {
1314:                    // Hack: simulate border-collapsing by setting cell spacing
1315:                    // to 0. Once I implement "real" border collapse, I should
1316:                    // take this out.
1317:                    cellSpacing = 0;
1318:                }
1319:
1320:                cells = new CellBox[rows][columns];
1321:                rowspans = new int[rows][columns];
1322:                colspans = new int[rows][columns];
1323:                rowElements = new Element[rows];
1324:                createCells();
1325:
1326:                // Create the actual cell contents
1327:                // We do this in reverse row order (last to first) for the following
1328:                // reason: a JSF component, such as an output text, can be replicated
1329:                // on every row by a JSF Data Table. For incremental layout purposes,
1330:                // I stash the box associated with the LiveBean right with the
1331:                // LiveBean. This box will always be the last-rendered box for the
1332:                // LiveBean. (There's a way to turn this off, used by the paint
1333:                // preview methods in PageBox).  However, when we're trying to
1334:                // look up the position for a replicated component that's in a table
1335:                // we want to show the entry on the FIRST row, not the last!
1336:                // Therefore, render in reverse order. Ditto for horizontal order,
1337:                // in case a JSF component were to replicate components horizontally
1338:                // too!
1339:                // XXX Does this mess up in-order caret traversal of the table??
1340:                finishLineBox(context); // ensure that cell has its own linebox
1341:
1342:                for (int i = rows - 1; i >= 0; i--) {
1343:                    //for (int j = columns-1; j >= 0; j--) {
1344:                    for (int j = 0; j < columns; j++) {
1345:                        CellBox box = cells[i][j];
1346:
1347:                        if ((box == null) || (box == OCCUPIED)) {
1348:                            continue;
1349:                        }
1350:
1351:                        box.createChildren(context);
1352:                        finishLineBox(context); // ensure that content outside doesn't spill into cell linebox
1353:
1354:                        // XXX TODO - I should be clearing all floats here too!
1355:                    }
1356:                }
1357:
1358:                if ((caption != null) && !captionAbove) {
1359:                    int i = getBoxCount();
1360:                    addNode(context, caption, null, null, null);
1361:                    finishLineBox(context);
1362:
1363:                    // Add boxes added for the caption into the caption box list
1364:                    int n = getBoxCount();
1365:                    captionBoxes = new ArrayList<CssBox>(n - i);
1366:
1367:                    for (; i < n; i++) {
1368:                        captionBoxes.add(getBox(i));
1369:                    }
1370:                }
1371:            }
1372:
1373:            /** Add a caption box, if applicable */
1374:            private static Element findCaption(Element table) {
1375:                // The <caption> element is supposed to be the first child.
1376:                // At least it was in html 4.0:
1377:                // http://www.w3.org/TR/REC-html40/struct/tables.html#h-11.2.2
1378:                // TODO - make sure this is still required in xhtml.
1379:                NodeList list = table.getChildNodes();
1380:                Element caption = null;
1381:
1382:                for (int i = 0, n = list.getLength(); i < n; i++) {
1383:                    Node node = (Node) list.item(i);
1384:
1385:                    if (node.getNodeType() == Node.ELEMENT_NODE) {
1386:                        Element e = (Element) node;
1387:
1388:                        //                if (CssLookup.getValue(e, XhtmlCss.DISPLAY_INDEX) == CssValueConstants.TABLE_CAPTION_VALUE) {
1389:                        if (CssProvider.getValueService().isTableCaptionValue(
1390:                                CssProvider.getEngineService()
1391:                                        .getComputedValueForElement(e,
1392:                                                XhtmlCss.DISPLAY_INDEX))) {
1393:                            caption = (Element) node;
1394:                        }
1395:
1396:                        // Caption must be first element
1397:                        break;
1398:                    }
1399:                }
1400:
1401:                return caption;
1402:            }
1403:
1404:            /**
1405:             * Compute a cell 2d array, and initialize it with boxes for all
1406:             * the locations that have nonempty content. The boxes will point
1407:             * to their elements, but are not formatted yet (the format depends
1408:             * on column widths which have not yet been computed).
1409:             */
1410:            private void createCells() {
1411:                setProbableChildCount(rows * columns);
1412:
1413:                int row = 0;
1414:
1415:                NodeList list = table.getChildNodes();
1416:                int len = list.getLength();
1417:                int footerBegin = -1;
1418:                int footerEnd = -1;
1419:
1420:                for (int i = 0; i < len; i++) {
1421:                    Node trn = (Node) list.item(i);
1422:
1423:                    if (trn.getNodeType() != Node.ELEMENT_NODE) {
1424:                        continue;
1425:                    }
1426:
1427:                    Element tr = (Element) trn;
1428:                    //            Value display = CssLookup.getValue(tr, XhtmlCss.DISPLAY_INDEX);
1429:                    CssValue cssDisplay = CssProvider.getEngineService()
1430:                            .getComputedValueForElement(tr,
1431:                                    XhtmlCss.DISPLAY_INDEX);
1432:
1433:                    //            if (display == CssValueConstants.TABLE_ROW_VALUE) {
1434:                    if (CssProvider.getValueService().isTableRowValue(
1435:                            cssDisplay)) {
1436:                        createRowCells(row, tr);
1437:                        row++;
1438:                        //            } else if ((display == CssValueConstants.TABLE_ROW_GROUP_VALUE) ||
1439:                        //                    (display == CssValueConstants.TABLE_HEADER_GROUP_VALUE) ||
1440:                        //                    (display == CssValueConstants.TABLE_FOOTER_GROUP_VALUE)) {
1441:                    } else if (CssProvider.getValueService()
1442:                            .isTableRowGroupValue(cssDisplay)
1443:                            || CssProvider.getValueService()
1444:                                    .isTableHeaderGroupValue(cssDisplay)
1445:                            || CssProvider.getValueService()
1446:                                    .isTableFooterGroupValue(cssDisplay)) {
1447:                        //                boolean isFooter = display == CssValueConstants.TABLE_FOOTER_GROUP_VALUE;
1448:                        boolean isFooter = CssProvider.getValueService()
1449:                                .isTableFooterGroupValue(cssDisplay);
1450:
1451:                        if (isFooter) {
1452:                            assert footerBegin == -1; // Only one tfoot is allowed!
1453:
1454:                            // Should we emit a warning for the user here?
1455:                            footerBegin = row;
1456:                        }
1457:
1458:                        NodeList list2 = tr.getChildNodes();
1459:                        int len2 = list2.getLength();
1460:
1461:                        for (int j = 0; j < len2; j++) {
1462:                            Node trn2 = (Node) list2.item(j);
1463:
1464:                            if (trn2.getNodeType() != Node.ELEMENT_NODE) {
1465:                                continue;
1466:                            }
1467:
1468:                            Element tr2 = (Element) trn2;
1469:
1470:                            //                    if (CssLookup.getValue(tr2, XhtmlCss.DISPLAY_INDEX) == CssValueConstants.TABLE_ROW_VALUE) {
1471:                            if (CssProvider.getValueService().isTableRowValue(
1472:                                    CssProvider.getEngineService()
1473:                                            .getComputedValueForElement(tr2,
1474:                                                    XhtmlCss.DISPLAY_INDEX))) {
1475:                                createRowCells(row, tr2);
1476:                                row++;
1477:                            }
1478:                        }
1479:
1480:                        if (isFooter) {
1481:                            footerEnd = row;
1482:                        }
1483:                    }
1484:                }
1485:
1486:                // Move footer rows to the end
1487:                // This is hacky. I should support rowgroups instead; then it
1488:                // would be a simple rowgroup swap!
1489:                if ((footerBegin != -1) && (footerBegin != footerEnd)) {
1490:                    int footerLength = footerEnd - footerBegin;
1491:                    int targetRow = rows - footerLength;
1492:
1493:                    for (int i = 0; i < footerLength; i++) {
1494:                        swapRow(footerBegin + i, targetRow + i);
1495:                    }
1496:                }
1497:            }
1498:
1499:            private void swapRow(int a, int b) {
1500:                for (int i = 0; i < columns; i++) {
1501:                    CellBox tempBox = cells[b][i];
1502:                    cells[b][i] = cells[a][i];
1503:                    cells[a][i] = tempBox;
1504:
1505:                    int temp = rowspans[b][i];
1506:                    rowspans[b][i] = rowspans[a][i];
1507:                    rowspans[a][i] = temp;
1508:
1509:                    temp = colspans[b][i];
1510:                    colspans[b][i] = colspans[a][i];
1511:                    colspans[a][i] = temp;
1512:                }
1513:
1514:                Element tempE = rowElements[a];
1515:                rowElements[a] = rowElements[b];
1516:                rowElements[b] = tempE;
1517:            }
1518:
1519:            private void createRowCells(int row, Element tr) {
1520:                rowElements[row] = tr;
1521:
1522:                NodeList list2 = tr.getChildNodes();
1523:                int len2 = list2.getLength();
1524:                int col = 0;
1525:
1526:                for (int j = 0; j < len2; j++) {
1527:                    Node tdthn = (Node) list2.item(j);
1528:
1529:                    if (tdthn.getNodeType() != Node.ELEMENT_NODE) {
1530:                        continue;
1531:                    }
1532:
1533:                    Element tdth = (Element) tdthn;
1534:                    //            Value display = CssLookup.getValue(tdth, XhtmlCss.DISPLAY_INDEX);
1535:                    CssValue cssDisplay = CssProvider.getEngineService()
1536:                            .getComputedValueForElement(tdth,
1537:                                    XhtmlCss.DISPLAY_INDEX);
1538:
1539:                    //            if (display != CssValueConstants.TABLE_CELL_VALUE) {
1540:                    if (!CssProvider.getValueService().isTableCellValue(
1541:                            cssDisplay)) {
1542:                        continue;
1543:                    }
1544:
1545:                    while ((col < columns) && (cells[row][col] == OCCUPIED)) {
1546:                        col++;
1547:                    }
1548:
1549:                    col += createCell(tdth, row, col);
1550:                }
1551:            }
1552:
1553:            /** Create the given cell, and return its colspan */
1554:            private int createCell(Element tdth, int row, int col) {
1555:                if (col == columns) {
1556:                    // UGH! The table is "invalid" in that the computed column
1557:                    // count is less than the actual columns found. This can
1558:                    // only happen when we've counted columns using <col> and
1559:                    // <colgroup> elements, which the spec says that should
1560:                    // uniquely identify the number of columns. But users may
1561:                    // have thrown in additional columns in subsequent rows anyway,
1562:                    // so try to deal with that.
1563:                    // For now, we can't really - we'd have to resize the
1564:                    // cells, rowspan and colspan parameters - but they are
1565:                    // parameters, not fields. Another possibility is to
1566:                    // bump up the column count and try again.
1567:                    // For now, just bail - which will truncate overflow
1568:                    // cells.  Hopefully this will clue the user in to the
1569:                    // fact that the user has an error in the table
1570:                    // definition.
1571:                    return 0;
1572:                }
1573:
1574:                int colspan = HtmlAttribute.getIntegerAttributeValue(tdth,
1575:                        HtmlAttribute.COLSPAN, 1);
1576:                int rowspan = HtmlAttribute.getIntegerAttributeValue(tdth,
1577:                        HtmlAttribute.ROWSPAN, 1);
1578:
1579:                // Look for errors in the table definition
1580:                if (colspan <= 0) { // HTML 11.2.6: "0" means fill the row. TODO.
1581:                    colspan = 1;
1582:                }
1583:
1584:                if (rowspan <= 0) { // HTML 11.2.6: "0" means fill the column. TODO.
1585:                    rowspan = 1;
1586:                }
1587:
1588:                if ((col + colspan) > columns) {
1589:                    colspan = columns - col;
1590:                }
1591:
1592:                if ((row + rowspan) > rows) {
1593:                    rowspan = rows - row;
1594:                }
1595:
1596:                colspans[row][col] = colspan;
1597:                rowspans[row][col] = rowspan;
1598:
1599:                CellBox box = new CellBox(webform, tdth, BoxType.STATIC, false,
1600:                        this );
1601:                box.row = row;
1602:                box.col = col;
1603:
1604:                for (int m = 0; m < rowspan; m++) {
1605:                    for (int n = 0; n < colspan; n++) {
1606:                        cells[row + m][col + n] = OCCUPIED;
1607:                    }
1608:                }
1609:
1610:                cells[row][col] = box;
1611:
1612:                box.initialize();
1613:
1614:                addBox(box, null, null);
1615:
1616:                // We create the children of the box itself when we're done with
1617:                // this
1618:                return colspan;
1619:            }
1620:
1621:            /** Format all the cells in the table */
1622:            private void formatCells(int[] columnWidths, FormatContext context) {
1623:                // The table cells should not be affected by floats in effect
1624:                List<FloatingBoxInfo> oldFloats = context.floats;
1625:                context.floats = null;
1626:
1627:                boolean oldFloating = context.floating;
1628:                context.floating = false;
1629:
1630:                // Format all the cells in the table
1631:                for (int i = 0; i < rows; i++) {
1632:                    for (int j = 0; j < columns; j++) {
1633:                        CellBox box = cells[i][j];
1634:
1635:                        if ((box != null) && (box != OCCUPIED)) {
1636:                            formatCell(i, j, columnWidths, context);
1637:                        }
1638:                    }
1639:                }
1640:
1641:                context.floats = oldFloats;
1642:                context.floating = oldFloating;
1643:            }
1644:
1645:            private void formatCell(int row, int col, int[] columnWidths,
1646:                    FormatContext context) {
1647:                CellBox box = cells[row][col];
1648:                int colspan = colspans[row][col];
1649:
1650:                //int rowspan = rowspans[row][col];
1651:                int w = 0;
1652:
1653:                for (int m = 0; m < colspan; m++) {
1654:                    w += columnWidths[col + m];
1655:                }
1656:
1657:                int ac = w;
1658:                int ah = containingBlockHeight / columns;
1659:                box.setContainingBlock(0, 0, ac, ah);
1660:                box.contentWidth = ac - box.leftBorderWidth - box.leftPadding
1661:                        - box.rightPadding - box.rightBorderWidth;
1662:                box.contentHeight = ah - box.topBorderWidth - box.topPadding
1663:                        - box.bottomPadding - box.bottomBorderWidth;
1664:
1665:                box.relayout(context);
1666:
1667:                // XXX why am I doing this?
1668:                box.contentWidth = ac;
1669:                box.contentHeight = AUTO;
1670:
1671:                box.inline = false;
1672:                box.replaced = false;
1673:
1674:                box.boxType = BoxType.STATIC; // prevent "bad" docs from screwing things up
1675:
1676:                // by setting position: absolute on <td>'s for example
1677:                box.computeVerticalLengths(context);
1678:
1679:                box.height = box.topBorderWidth + box.topPadding
1680:                        + box.contentHeight + box.bottomPadding
1681:                        + box.bottomBorderWidth + cellSpacing;
1682:
1683:                // All floats must be cleared; nothing can extend outside the cell
1684:                //        box.clearBottom(context, CssValueConstants.BOTH_VALUE);
1685:                box.clearBottom(context, CssProvider.getValueService()
1686:                        .getBothCssValueConstant());
1687:                box.originalHeight = box.height;
1688:
1689:                // According to the table spec (CSS2.1 section 17.5.3) the content height used
1690:                // for table cells should be the max of the specified height and the computed
1691:                // minimum required height
1692:                //        int boxHeight = CssLookup.getLength(box.getElement(), XhtmlCss.HEIGHT_INDEX);
1693:                int boxHeight = CssUtilities.getCssLength(box.getElement(),
1694:                        XhtmlCss.HEIGHT_INDEX);
1695:
1696:                if ((boxHeight != AUTO) && (boxHeight > box.contentHeight)) {
1697:                    box.contentHeight = boxHeight;
1698:                    box.height = box.topBorderWidth + box.topPadding
1699:                            + box.contentHeight + box.bottomPadding
1700:                            + box.bottomBorderWidth + cellSpacing;
1701:                }
1702:
1703:                //only now, when we calculated the height, we can move the relatives
1704:                box.finishAllRelatives(context);
1705:            }
1706:
1707:            private static int countRow(Element row) {
1708:                int rowcols = 0; // columns for this row
1709:
1710:                NodeList list2 = row.getChildNodes();
1711:                int len2 = list2.getLength();
1712:
1713:                for (int j = 0; j < len2; j++) {
1714:                    Node tdthn = (Node) list2.item(j);
1715:
1716:                    if (tdthn.getNodeType() != Node.ELEMENT_NODE) {
1717:                        continue;
1718:                    }
1719:
1720:                    Element tdth = (Element) tdthn;
1721:                    //            Value display = CssLookup.getValue(tdth, XhtmlCss.DISPLAY_INDEX);
1722:                    CssValue cssDisplay = CssProvider.getEngineService()
1723:                            .getComputedValueForElement(tdth,
1724:                                    XhtmlCss.DISPLAY_INDEX);
1725:
1726:                    //            if (display != CssValueConstants.TABLE_CELL_VALUE) {
1727:                    if (!CssProvider.getValueService().isTableCellValue(
1728:                            cssDisplay)) {
1729:                        continue;
1730:                    }
1731:
1732:                    int colspan = HtmlAttribute.getIntegerAttributeValue(tdth,
1733:                            HtmlAttribute.COLSPAN, 1);
1734:
1735:                    if (colspan <= 0) {
1736:                        // HTML 11.2.6: "The value zero ("0") means that the cell spans all 
1737:                        // columns from the current column to the last column of the column 
1738:                        // group (COLGROUP) in which the cell is defined."
1739:                        // I don't have a good way to simulate this. But for column count purposes
1740:                        // we'll consider it as 1.
1741:                        // I'm picking up negative values for this too to avoid badly
1742:                        // constructed pages from breaking rendering.
1743:                        colspan = 1;
1744:                    }
1745:
1746:                    rowcols += colspan;
1747:                }
1748:
1749:                return rowcols;
1750:            }
1751:
1752:            /**
1753:             * Compute the number of columns.
1754:             * See section 19.2.1 in the XHTML Tables Module
1755:             *   http://www.w3.org/TR/xhtml2/mod-tables.html
1756:             */
1757:            private static int computeColumnCount(Element table) {
1758:                // If the table contains any colgroup or col elements, use those
1759:                // to compute the number of columns
1760:                // XXX Can these puppies (colgroup, col) appear anywhere on only
1761:                // as direct children of table or colgroup?
1762:                int columns = 0;
1763:
1764:                NodeList list = table.getChildNodes();
1765:                int len = list.getLength();
1766:
1767:                for (int i = 0; i < len; i++) {
1768:                    Node child = (Node) list.item(i);
1769:
1770:                    if (child.getNodeType() != Node.ELEMENT_NODE) {
1771:                        continue;
1772:                    }
1773:
1774:                    Element element = (Element) child;
1775:                    //            Value display = CssLookup.getValue(element, XhtmlCss.DISPLAY_INDEX);
1776:                    CssValue cssDisplay = CssProvider.getEngineService()
1777:                            .getComputedValueForElement(element,
1778:                                    XhtmlCss.DISPLAY_INDEX);
1779:
1780:                    //            if (display == CssValueConstants.TABLE_COLUMN_GROUP_VALUE) {
1781:                    if (CssProvider.getValueService().isTableColumnGroupValue(
1782:                            cssDisplay)) {
1783:                        // Sum up the spans of any <col> children of this
1784:                        // <colgroup>, and track whether we had any
1785:                        boolean empty = true;
1786:                        NodeList list2 = element.getChildNodes();
1787:                        int len2 = list2.getLength();
1788:
1789:                        for (int j = 0; j < len2; j++) {
1790:                            Node child2 = (Node) list2.item(j);
1791:
1792:                            if (child2.getNodeType() != Node.ELEMENT_NODE) {
1793:                                continue;
1794:                            }
1795:
1796:                            Element element2 = (Element) child2;
1797:
1798:                            //                    if (CssLookup.getValue(element2, XhtmlCss.DISPLAY_INDEX) == CssValueConstants.TABLE_COLUMN_VALUE) {
1799:                            if (CssProvider
1800:                                    .getValueService()
1801:                                    .isTableColumnValue(
1802:                                            CssProvider
1803:                                                    .getEngineService()
1804:                                                    .getComputedValueForElement(
1805:                                                            element2,
1806:                                                            XhtmlCss.DISPLAY_INDEX))) {
1807:                                empty = false;
1808:                                columns += HtmlAttribute
1809:                                        .getIntegerAttributeValue(element2,
1810:                                                HtmlAttribute.SPAN, 1);
1811:                            }
1812:                        }
1813:
1814:                        if (empty) {
1815:                            // For each empty colgroup element, take the value of 
1816:                            // its span attribute (default 1)
1817:                            columns += HtmlAttribute.getIntegerAttributeValue(
1818:                                    element, HtmlAttribute.SPAN, 1);
1819:                        } // else: for colgroups that have children col elements
1820:
1821:                        // we ignore the colgroup span attribute
1822:                        //            } else if (display == CssValueConstants.TABLE_COLUMN_VALUE) {
1823:                    } else if (CssProvider.getValueService()
1824:                            .isTableColumnValue(cssDisplay)) {
1825:                        columns += HtmlAttribute.getIntegerAttributeValue(
1826:                                element, HtmlAttribute.SPAN, 1);
1827:                    }
1828:                }
1829:
1830:                if (columns > 0) {
1831:                    // We had <colgroup> or <col> elements - which means we're
1832:                    // done. According to 19.2.1, it's an error if a table contains
1833:                    // colgroup or col elements and the rest of the table implies
1834:                    // a different number of columns than what is computed above
1835:                    return columns;
1836:                }
1837:
1838:                // No <colgroup> or <col> elements: have to count columns the
1839:                // "hard" way by looking at all table cells, computing the number
1840:                // of columns in each row and finding the maximum.
1841:                for (int i = 0; i < len; i++) {
1842:                    Node trn = (Node) list.item(i);
1843:
1844:                    if (trn.getNodeType() != Node.ELEMENT_NODE) {
1845:                        continue;
1846:                    }
1847:
1848:                    Element tr = (Element) trn;
1849:                    //            Value display = CssLookup.getValue(tr, XhtmlCss.DISPLAY_INDEX);
1850:                    CssValue cssDisplay = CssProvider.getEngineService()
1851:                            .getComputedValueForElement(tr,
1852:                                    XhtmlCss.DISPLAY_INDEX);
1853:
1854:                    //            if (display == CssValueConstants.TABLE_ROW_VALUE) {
1855:                    if (CssProvider.getValueService().isTableRowValue(
1856:                            cssDisplay)) {
1857:                        int rowcols = countRow(tr); // columns for this row
1858:
1859:                        if (rowcols > columns) {
1860:                            columns = rowcols;
1861:                        }
1862:                        //            } else if ((display == CssValueConstants.TABLE_ROW_GROUP_VALUE) ||
1863:                        //                    (display == CssValueConstants.TABLE_HEADER_GROUP_VALUE) ||
1864:                        //                    (display == CssValueConstants.TABLE_FOOTER_GROUP_VALUE)) {
1865:                    } else if (CssProvider.getValueService()
1866:                            .isTableRowGroupValue(cssDisplay)
1867:                            || CssProvider.getValueService()
1868:                                    .isTableHeaderGroupValue(cssDisplay)
1869:                            || CssProvider.getValueService()
1870:                                    .isTableFooterGroupValue(cssDisplay)) {
1871:                        NodeList list2 = tr.getChildNodes();
1872:                        int len2 = list2.getLength();
1873:
1874:                        for (int j = 0; j < len2; j++) {
1875:                            Node trn2 = (Node) list2.item(j);
1876:
1877:                            if (trn2.getNodeType() != Node.ELEMENT_NODE) {
1878:                                continue;
1879:                            }
1880:
1881:                            Element tr2 = (Element) trn2;
1882:
1883:                            //                    if (CssLookup.getValue(tr2, XhtmlCss.DISPLAY_INDEX) == CssValueConstants.TABLE_ROW_VALUE) {
1884:                            if (CssProvider.getValueService().isTableRowValue(
1885:                                    CssProvider.getEngineService()
1886:                                            .getComputedValueForElement(tr2,
1887:                                                    XhtmlCss.DISPLAY_INDEX))) {
1888:                                int rowcols = countRow(tr2);
1889:
1890:                                if (rowcols > columns) {
1891:                                    columns = rowcols;
1892:                                }
1893:                            }
1894:                        }
1895:                    }
1896:                }
1897:
1898:                return columns;
1899:            }
1900:
1901:            /**
1902:             * Compute the number of rows; this is just the number
1903:             * of <tr>'s in the table; these may be nested within
1904:             * <thead>, <tbody> and <tfoot> tags.
1905:             */
1906:            private static int computeRowCount(Element table) {
1907:                // Count number of <tr> - either directly below <table>, or
1908:                // within <thead>, <tbody> or <tfoot>
1909:                int rows = 0;
1910:                NodeList list = table.getChildNodes();
1911:                int len = list.getLength();
1912:
1913:                for (int i = 0; i < len; i++) {
1914:                    Node trn = (Node) list.item(i);
1915:
1916:                    if (trn.getNodeType() != Node.ELEMENT_NODE) {
1917:                        continue;
1918:                    }
1919:
1920:                    Element tr = (Element) trn;
1921:                    //            Value display = CssLookup.getValue(tr, XhtmlCss.DISPLAY_INDEX);
1922:                    CssValue cssDisplay = CssProvider.getEngineService()
1923:                            .getComputedValueForElement(tr,
1924:                                    XhtmlCss.DISPLAY_INDEX);
1925:
1926:                    //            if (display == CssValueConstants.TABLE_ROW_VALUE) {
1927:                    if (CssProvider.getValueService().isTableRowValue(
1928:                            cssDisplay)) {
1929:                        rows++;
1930:                        //            } else if ((display == CssValueConstants.TABLE_ROW_GROUP_VALUE) ||
1931:                        //                    (display == CssValueConstants.TABLE_HEADER_GROUP_VALUE) ||
1932:                        //                    (display == CssValueConstants.TABLE_FOOTER_GROUP_VALUE)) {
1933:                    } else if (CssProvider.getValueService()
1934:                            .isTableRowGroupValue(cssDisplay)
1935:                            || CssProvider.getValueService()
1936:                                    .isTableHeaderGroupValue(cssDisplay)
1937:                            || CssProvider.getValueService()
1938:                                    .isTableFooterGroupValue(cssDisplay)) {
1939:                        NodeList list2 = tr.getChildNodes();
1940:                        int len2 = list2.getLength();
1941:
1942:                        for (int j = 0; j < len2; j++) {
1943:                            Node trn2 = (Node) list2.item(j);
1944:
1945:                            if (trn2.getNodeType() != Node.ELEMENT_NODE) {
1946:                                continue;
1947:                            }
1948:
1949:                            Element tr2 = (Element) trn2;
1950:
1951:                            //                    if (CssLookup.getValue(tr2, XhtmlCss.DISPLAY_INDEX) == CssValueConstants.TABLE_ROW_VALUE) {
1952:                            if (CssProvider.getValueService().isTableRowValue(
1953:                                    CssProvider.getEngineService()
1954:                                            .getComputedValueForElement(tr2,
1955:                                                    XhtmlCss.DISPLAY_INDEX))) {
1956:                                rows++;
1957:                            }
1958:                        }
1959:                    }
1960:                }
1961:
1962:                return rows;
1963:            }
1964:
1965:            //    public String toString() {
1966:            //        return "TableBox[" + paramString() + "]";
1967:            //    }
1968:
1969:            protected String paramString() {
1970:                return /* super.paramString() + ", " + */"rows=" + rows + ", "
1971:                        + "columns=" + columns // NOI18N
1972:                        + ", element=" + table + ", x=" + x + ", y=" + y // NOI18N
1973:                        + ", size=" + width + ":" + height // NOI18N
1974:                        + ", contentWidth=" + contentWidth // NOI18N
1975:                        + ", containingBlockWidth=" + containingBlockWidth; // NOI18N
1976:
1977:            }
1978:
1979:            public void relayout(FormatContext context) {
1980:                // Ensure that createChildren has run and has initialized
1981:                // the row and column fields
1982:                assert (rows != -1) && (columns != -1);
1983:
1984:                if ((columns == 0) || (rows == 0)) {
1985:                    rows = columns = 0;
1986:                    positionCells(new int[0], context);
1987:
1988:                    //            int width = CssLookup.getLength(table, XhtmlCss.WIDTH_INDEX);
1989:                    //            int height = CssLookup.getLength(table, XhtmlCss.HEIGHT_INDEX);
1990:                    int width = CssUtilities.getCssLength(table,
1991:                            XhtmlCss.WIDTH_INDEX);
1992:                    int height = CssUtilities.getCssLength(table,
1993:                            XhtmlCss.HEIGHT_INDEX);
1994:
1995:                    if (width != AUTO) {
1996:                        contentWidth = super .width = width;
1997:                    }
1998:
1999:                    if (height != AUTO) {
2000:                        contentHeight = super .height = height;
2001:                    }
2002:
2003:                    return;
2004:                }
2005:
2006:                int[] columnWidths = new int[columns];
2007:
2008:                effectiveTopMargin = topMargin;
2009:                effectiveBottomMargin = bottomMargin;
2010:
2011:                if (fixedLayout) {
2012:                    fixedLayout(columnWidths, context);
2013:                } else {
2014:                    autoLayout(columnWidths, context);
2015:                }
2016:
2017:            }
2018:
2019:            /**
2020:             * Implement the automatic table layout algorithm,
2021:             * described in the CSS2.1 spec:
2022:             * http://www.w3.org/TR/CSS21/tables.html#auto-table-layout
2023:             */
2024:            private void autoLayout(int[] columnWidths, FormatContext context) {
2025:                assert (rows > 0) && (columns > 0);
2026:
2027:                computeAutoColumnWidths(columnWidths, context, false);
2028:                formatCells(columnWidths, context);
2029:                positionCells(columnWidths, context);
2030:            }
2031:
2032:            /** Compute the width of the table. May return AUTO.
2033:             * @param erase If set, "erase" the value from the CSS cached data
2034:             *    when done with it. Typically set when we're just scanning the
2035:             *    preferred width (when the containing block may be 0) so we
2036:             *    want to recompute when we know the real containing block width.
2037:             */
2038:            private int getTableWidth(boolean erase) {
2039:                //  From
2040:                // http://www.nic.fi/~tapio1/Teaching/Taulukot3.php3:
2041:                //
2042:                //    In principle the width property means also in tables the
2043:                //    content width. Because the element TABLE doesn't have
2044:                //    direct actual content (between the actual content is at
2045:                //    least one TR element), in ordinary cases only borders
2046:                //    increase the total width of the block box of the table.
2047:                //
2048:                //    The problems is however the fact that in the HTML 4.01
2049:                //    specification calculating the width property of the TABLE
2050:                //    element is used another formula as calculating the width
2051:                //    property in CSS. In the HTML 4.01 specification has been
2052:                //    said about the attribute width following:
2053:                //         This attribute specifies the desired width of the
2054:                //         entire table...
2055:                //    According that definition borders are counted to the total
2056:                //    width of the table and it is not the content width like in
2057:                //    CSS.
2058:                //int tableWidth = Css.getLength(table, XhtmlCss.WIDTH_INDEX);
2059:                //        Value val = CssLookup.getValue(table, XhtmlCss.WIDTH_INDEX);
2060:                CssValue cssValue = CssProvider
2061:                        .getEngineService()
2062:                        .getComputedValueForElement(table, XhtmlCss.WIDTH_INDEX);
2063:
2064:                //        if (val == CssValueConstants.AUTO_VALUE) {
2065:                if (CssProvider.getValueService().isAutoValue(cssValue)) {
2066:                    return AUTO;
2067:                } else {
2068:                    //            int tableWidth = (int)val.getFloatValue();
2069:                    int tableWidth = (int) cssValue.getFloatValue();
2070:
2071:                    // Empirically I've noticed mozilla treats tables differently; 
2072:                    // than regular boxes: the width: property affects the final
2073:                    // width of the table (including borders and padding).
2074:                    tableWidth -= (leftBorderWidth + leftPadding + rightPadding + rightBorderWidth);
2075:
2076:                    // Each of the cells will absorb the space for the top and left
2077:                    // padding area. However, we also need to have a padding area
2078:                    // on the right side of the rightmost column, so account for
2079:                    // that space here.
2080:                    tableWidth -= cellSpacing;
2081:
2082:                    if (erase) {
2083:                        //                boolean wasPercentage =
2084:                        //                    val instanceof ComputedValue &&
2085:                        //                    (((ComputedValue)val).getCascadedValue().getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE);
2086:                        boolean wasPercentage = cssValue instanceof  CssComputedValue
2087:                                && CssProvider.getValueService()
2088:                                        .isOfPrimitivePercentageType(
2089:                                                ((CssComputedValue) cssValue)
2090:                                                        .getCascadedValue());
2091:
2092:                        if (wasPercentage) {
2093:                            //                    CssLookup.uncompute(table, XhtmlCss.WIDTH_INDEX);
2094:                            CssProvider.getEngineService()
2095:                                    .uncomputeValueForElement(table,
2096:                                            XhtmlCss.WIDTH_INDEX);
2097:                        }
2098:                    }
2099:
2100:                    return tableWidth;
2101:                }
2102:            }
2103:
2104:            private void computeAutoColumnWidths(int[] columnWidths,
2105:                    FormatContext context, boolean scan) {
2106:                int tableWidth = getTableWidth(scan);
2107:
2108:                // The minimum width that is required to layout the content, 
2109:                // all linebreak possibilities will be used
2110:                int[][] minCellWidths = new int[rows][columns];
2111:
2112:                // The width the content could fill without any linebreaks.
2113:                int[][] maxCellWidths = new int[rows][columns];
2114:
2115:                int fixedWidthColumns = 0;
2116:                boolean[] fixedWidths = new boolean[columns];
2117:
2118:                /** Special meaning: positive numbers: fixed width. Negative
2119:                 * numbers: negative percentage. AUTO: unconstrained
2120:                 * (default) */
2121:                int[] constraints = new int[columns];
2122:
2123:                for (int i = 0; i < columns; i++) {
2124:                    columnWidths[i] = AUTO;
2125:                    constraints[i] = AUTO;
2126:                }
2127:
2128:                // From RFC 1942:
2129:                // In the first pass, line wrapping is disabled, and the user
2130:                // agent keeps track of the minimum and maximum width of each
2131:                // cell. The maximum width is given by the widest line. As line
2132:                // wrap has been disabled, paragraphs are treated as long lines
2133:                // unless broken by <BR> elements. The minimum width is given by
2134:                // the widest word or image etc.  taking into account leading
2135:                // indents and list bullets etc. In other words, if you were to
2136:                // format the cell's content in a window of its own, determine
2137:                // the minimum width you could make the window before the cell
2138:                // begins to overflow. Allowing user agents to split words will
2139:                // minimize the need for horizontal scrolling or in the worst
2140:                // case clipping of cell contents.
2141:                // The comments in this section (the numbered steps) is literally
2142:                // the text from the CSS2 spec which lists the automatic table
2143:                // algorithm
2144:                // 1. Calculate the minimum content width (MCW) of each cell:
2145:                // the formatted content may span any number of lines but may
2146:                // not overflow the cell box. If the specified 'width' (W) of
2147:                // the cell is greater than MCW, W is the minimum cell width. A
2148:                // value of 'auto' means that MCW is the minimum cell width.
2149:                for (int i = 0; i < rows; i++) {
2150:                    for (int j = 0; j < columns; j++) {
2151:                        CellBox box = cells[i][j];
2152:
2153:                        // XXX do I need to do layout on the cells first?
2154:                        if ((box == null) || (box == OCCUPIED)) {
2155:                            continue;
2156:                        }
2157:
2158:                        // Ensure that margins etc. have been initialized
2159:                        if (context != null) {
2160:                            box.initializeHorizontalWidths(context);
2161:                        }
2162:
2163:                        int mcw = box.getPrefMinWidth();
2164:
2165:                        Element element = box.getElement();
2166:                        //                Value value = CssLookup.getValue(element, XhtmlCss.WIDTH_INDEX);
2167:                        CssValue cssValue = CssProvider.getEngineService()
2168:                                .getComputedValueForElement(element,
2169:                                        XhtmlCss.WIDTH_INDEX);
2170:                        boolean wasPercentage = false;
2171:
2172:                        //                if (value != CssValueConstants.AUTO_VALUE) {
2173:                        if (!CssProvider.getValueService()
2174:                                .isAutoValue(cssValue)) {
2175:                            if (colspans[i][j] == 1) { // Only count fixed columns when I -know- it applies to this one
2176:                                fixedWidthColumns++;
2177:
2178:                                if (!fixedWidths[j]) {
2179:                                    fixedWidths[j] = true;
2180:                                }
2181:                            }
2182:
2183:                            //                    wasPercentage =
2184:                            //                        value instanceof ComputedValue &&
2185:                            //                        (((ComputedValue)value).getCascadedValue().getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE);
2186:                            wasPercentage = cssValue instanceof  CssComputedValue
2187:                                    && CssProvider
2188:                                            .getValueService()
2189:                                            .isOfPrimitivePercentageType(
2190:                                                    ((CssComputedValue) cssValue)
2191:                                                            .getCascadedValue());
2192:
2193:                            if (wasPercentage) {
2194:                                //                        int percentage =
2195:                                //                            (int)((ComputedValue)value).getCascadedValue().getFloatValue();
2196:                                int percentage = (int) ((CssComputedValue) cssValue)
2197:                                        .getCascadedValue().getFloatValue();
2198:
2199:                                if (percentage < 0) {
2200:                                    percentage = 0;
2201:                                }
2202:
2203:                                // Empirically (firefox), percentages win over fixed
2204:                                if ((constraints[j] == AUTO)
2205:                                        || (constraints[j] >= 0)
2206:                                        || (percentage > -constraints[j])) {
2207:                                    constraints[j] = -percentage;
2208:                                }
2209:                            } else {
2210:                                // Only replace auto or other (smaller) fixed widths
2211:                                // positive numbers indicates actual length
2212:                                //                        int length = (int)value.getFloatValue();
2213:                                //                        int length = (int)cssValue.getFloatValue();
2214:                                // XXX #126240 Possible NPE
2215:                                int length = cssValue == null ? null
2216:                                        : (int) cssValue.getFloatValue();
2217:
2218:                                if (length < 0) {
2219:                                    length = 0;
2220:                                }
2221:
2222:                                if ((constraints[j] == AUTO)
2223:                                        || ((constraints[j] >= 0) && (constraints[j] < length))) {
2224:                                    constraints[j] = length;
2225:                                }
2226:                            }
2227:                        }
2228:
2229:                        // XXX Are the border widths and paddings okay to use at this
2230:                        // point - have they been initialized? I suppose they have, but
2231:                        // what are they relative to, if not the table containing block -
2232:                        // which hasn't been computed yet? They must be relative to the
2233:                        // table width! I may have to go and pre-look that up in my
2234:                        // box initializer, and make sure that box.initialize() on the
2235:                        // TD boxes use the correct "containing block" !
2236:                        // XXX NO - we've already added in padding etc. for getminwidth - so don't do it here.
2237:                        // Instead adjust w computation to do it too so we have an apples to oranges comparison
2238:                        // when picking max!
2239:                        minCellWidths[i][j] = mcw + box.leftBorderWidth
2240:                                + box.leftPadding + box.rightPadding
2241:                                + box.rightBorderWidth + cellSpacing;
2242:
2243:                        // Also, calculate the "maximum" cell width of each
2244:                        // cell: formatting the content without breaking lines
2245:                        // other than where explicit line breaks occur.
2246:                        int max = box.getPrefWidth();
2247:
2248:                        if (max < mcw) { // in case w was greater than mcw and max
2249:                            max = mcw;
2250:                        }
2251:
2252:                        maxCellWidths[i][j] = max + box.leftBorderWidth
2253:                                + box.leftPadding + box.rightPadding
2254:                                + box.rightBorderWidth + cellSpacing;
2255:                    }
2256:                }
2257:
2258:                // 2. For each column, determine a maximum and minimum column
2259:                // width from the cells that span only that column. The minimum
2260:                // is that required by the cell with the largest minimum cell
2261:                // width (or the column 'width', whichever is larger). The
2262:                // maximum is that required by the cell with the largest maximum
2263:                // cell width (or the column 'width', whichever is larger).
2264:                int[] columnMaxes = new int[columns];
2265:                int[] columnMins = new int[columns];
2266:
2267:                //int[] percentageMax = new int[columns];
2268:                //int[] percentageMin = new int[columns];
2269:                for (int j = 0; j < columns; j++) {
2270:                    int max = 0; //Integer.MIN_VALUE;
2271:                    int min = 0; //Integer.MIN_VALUE;
2272:
2273:                    for (int i = 0; i < rows; i++) {
2274:                        if ((cells[i][j] == null) || (cells[i][j] == OCCUPIED)) {
2275:                            continue;
2276:                        }
2277:
2278:                        if (colspans[i][j] == 1) {
2279:                            if (maxCellWidths[i][j] > max) {
2280:                                max = maxCellWidths[i][j];
2281:                            }
2282:
2283:                            if (minCellWidths[i][j] > min) {
2284:                                min = minCellWidths[i][j];
2285:                            }
2286:                        }
2287:                    }
2288:
2289:                    columnMaxes[j] = max;
2290:                    columnMins[j] = min;
2291:                }
2292:
2293:                // 3. For each cell that spans more than one column, increase
2294:                // the minimum widths of the columns it spans so that together,
2295:                // they are at least as wide as the cell. Do the same for the
2296:                // maximum widths. If possible, widen all spanned columns by
2297:                // approximately the same amount.
2298:                // XXX need clarification: "at least as wide as the cell" - is this
2299:                // referring to the minimum width (MCW) of the cell?
2300:                for (int j = 0; j < columns; j++) {
2301:                    for (int i = 0; i < rows; i++) {
2302:                        if ((cells[i][j] == null) || (cells[i][j] == OCCUPIED)) {
2303:                            continue;
2304:                        }
2305:
2306:                        if (colspans[i][j] > 1) {
2307:                            int minCellWidth = minCellWidths[i][j];
2308:                            int maxCellWidth = maxCellWidths[i][j];
2309:                            int n = colspans[i][j];
2310:
2311:                            int minSum = 0;
2312:                            int maxSum = 0;
2313:
2314:                            for (int k = 0; k < n; k++) {
2315:                                minSum += columnMins[k];
2316:                                maxSum += columnMaxes[k];
2317:                            }
2318:
2319:                            if (minSum < minCellWidth) {
2320:                                int addition = (minCellWidth - minSum) / n;
2321:
2322:                                for (int k = 0; k < n; k++) {
2323:                                    columnMins[k] += addition;
2324:                                }
2325:                            }
2326:
2327:                            if (maxSum < maxCellWidth) {
2328:                                int addition = (maxCellWidth - maxSum) / n;
2329:
2330:                                for (int k = 0; k < n; k++) {
2331:                                    columnMaxes[k] += addition;
2332:                                }
2333:                            }
2334:                        }
2335:                    }
2336:                }
2337:
2338:                // This gives a maximum and minimum width for each
2339:                // column. Column widths influence the final table width as
2340:                // follows:
2341:                // 1. If the 'table' or 'inline-table' element's 'width'
2342:                // property has a computed value (W) other than 'auto', the
2343:                // property's value as used for layout is the greater of W and
2344:                // the minimum width required by all the columns plus cell
2345:                // spacing or borders (MIN). If W is greater than MIN, the extra
2346:                // width should be distributed over the columns.  
2347:                if (tableWidth != AUTO) {
2348:                    int min = 0;
2349:
2350:                    for (int k = 0; k < columns; k++) {
2351:                        min += columnMins[k];
2352:                    }
2353:
2354:                    if (min >= tableWidth) {
2355:                        tableWidth = min;
2356:
2357:                        // Assign column widths that are the minimums
2358:                        for (int k = 0; k < columns; k++) {
2359:                            columnWidths[k] = columnMins[k];
2360:                        }
2361:
2362:                        // TODO: should I allow tables to have arbitrary sizes?
2363:                        // If so, I should instead go and proportionally subtract
2364:                        // space here to make the table fit!!  However, the automatic
2365:                        // table layout algorithm doesn't call for that - and indeed
2366:                        // mozilla doesn't appear to behave that way either.
2367:                    } else if (min < tableWidth) {
2368:                        // if max < tableWidth too, I should go with the max widths,
2369:                        // and then tack on extra space!
2370:                        int max = 0;
2371:
2372:                        for (int k = 0; k < columns; k++) {
2373:                            if (constraints[k] == AUTO) {
2374:                                max += columnMaxes[k];
2375:                            } else {
2376:                                int portion = 0;
2377:
2378:                                if (constraints[k] < 0) {
2379:                                    // Percentage
2380:                                    int percentage = -constraints[k];
2381:                                    // #108602 When table is auto, count the pref width (here as max).
2382:                                    if (tableWidth == AUTO) {
2383:                                        portion = (percentage * columnMaxes[k]) / 100;
2384:                                    } else {
2385:                                        portion = (percentage * tableWidth) / 100;
2386:                                    }
2387:                                } else {
2388:                                    portion = constraints[k];
2389:                                }
2390:
2391:                                if (portion > columnMins[k]) {
2392:                                    max += portion;
2393:                                } else {
2394:                                    max += columnMins[k];
2395:                                }
2396:                            }
2397:                        }
2398:
2399:                        if (max < tableWidth) {
2400:                            for (int k = 0; k < columns; k++) {
2401:                                if (constraints[k] == AUTO) {
2402:                                    columnWidths[k] = columnMaxes[k];
2403:                                } else {
2404:                                    int portion = 0;
2405:
2406:                                    if (constraints[k] < 0) {
2407:                                        // Percentage
2408:                                        int percentage = -constraints[k];
2409:                                        // #108602 When table is auto, count the pref width (here as max).
2410:                                        if (tableWidth == AUTO) {
2411:                                            portion = (percentage * columnMaxes[k]) / 100;
2412:                                        } else {
2413:                                            portion = (percentage * tableWidth) / 100;
2414:                                        }
2415:                                    } else {
2416:                                        portion = constraints[k];
2417:                                    }
2418:
2419:                                    if (portion > columnMins[k]) {
2420:                                        columnWidths[k] = portion;
2421:                                    } else {
2422:                                        columnWidths[k] = columnMins[k];
2423:                                    }
2424:                                }
2425:                            }
2426:
2427:                            int leftOver = tableWidth - max;
2428:                            int assigned = 0;
2429:
2430:                            // Check for empty table, <table><tr><td/></tr></table>
2431:                            if (max == 0) {
2432:                                // Spread remainder out evenly
2433:                                distribute(columnWidths, leftOver, columns);
2434:                            } else {
2435:                                if ((fixedWidthColumns == columns)
2436:                                        || (fixedWidthColumns == 0)) {
2437:                                    // All (or none) of the columns have fixed width.
2438:                                    // Spread remainder out proportionally according
2439:                                    // to how much each column has already received.
2440:                                    // Thus with a 30% and a 70% column, the 70% column
2441:                                    // would receive 70% of the remainder as well.
2442:                                    for (int i = 0; i < columns; i++) {
2443:                                        int portion = (columnWidths[i] * leftOver)
2444:                                                / max;
2445:                                        columnWidths[i] += portion;
2446:                                        assigned += portion;
2447:                                    }
2448:
2449:                                    // remainder from rounding errors
2450:                                    columnWidths[0] += (leftOver - assigned);
2451:                                } else {
2452:                                    // Only some of the columns have fixed width; spread
2453:                                    // the available space among the non-fixed width columns.
2454:                                    // Arguably, percentages should be treated differently
2455:                                    // than fixed pixel widths but for now we treat them
2456:                                    // as the same (with the computed percentage value of the
2457:                                    // containing block)
2458:                                    int sum = 0;
2459:
2460:                                    for (int i = 0; i < columns; i++) {
2461:                                        if (!fixedWidths[i]) {
2462:                                            sum += columnWidths[i];
2463:                                        }
2464:                                    }
2465:
2466:                                    if (sum == 0) {
2467:                                        // Unexpected, but this can happen when we have large colspans
2468:                                        // etc. which means we have additional columns that have no
2469:                                        // content, so the additional "empty" columns are considered
2470:                                        // nonfixed. But in this case we really have the scenario
2471:                                        // where available space must be distributed among the other
2472:                                        // columns.
2473:                                        for (int i = 0; i < columns; i++) {
2474:                                            int portion = (columnWidths[i] * leftOver)
2475:                                                    / max;
2476:                                            columnWidths[i] += portion;
2477:                                            assigned += portion;
2478:                                        }
2479:                                    } else {
2480:                                        for (int i = 0; i < columns; i++) {
2481:                                            if (!fixedWidths[i]) {
2482:                                                int portion = (columnWidths[i] * leftOver)
2483:                                                        / sum;
2484:                                                columnWidths[i] += portion;
2485:                                                assigned += portion;
2486:                                            }
2487:                                        }
2488:                                    }
2489:
2490:                                    // remainder from rounding errors
2491:                                    columnWidths[0] += (leftOver - assigned);
2492:                                }
2493:                            }
2494:                        } else {
2495:                            // The actual table width is somewhere between
2496:                            // min and max. So assign the space above min 
2497:                            // out proportionally - except if a column reaches
2498:                            // its max, distributed the new extra space over
2499:                            // the other columns, until things stabilize.
2500:                            assignColumnWidths(tableWidth, min, columnWidths,
2501:                                    columnMins, columnMaxes, constraints);
2502:                        }
2503:                    }
2504:                } else {
2505:                    // 2. If the 'table' or 'inline-table' element has 'width:
2506:                    // auto', the table width used for layout is the greater of the
2507:                    // table's containing block width and MIN. However, if the
2508:                    // maximum width required by the columns plus cell spacing or
2509:                    // borders (MAX) is less than that of the containing block, use
2510:                    // MAX.
2511:                    int containing = containingBlockWidth
2512:                            - (leftBorderWidth + leftPadding + rightPadding
2513:                                    + rightBorderWidth + cellSpacing);
2514:
2515:                    if (computingPrefWidth) {
2516:                        // We want to pick the maxes when we're computing the
2517:                        // preferred width. I can't set the containingBlockWidth to
2518:                        // something large from within getPrefWidth because that
2519:                        // would be propagated into the cell children which may
2520:                        // do more dramatic things with the containingBlockWidth,
2521:                        // like have attachments to the right side
2522:                        containing = Integer.MAX_VALUE;
2523:                    }
2524:
2525:                    int max = 0;
2526:
2527:                    for (int k = 0; k < columns; k++) {
2528:                        if (constraints[k] == AUTO) {
2529:                            max += columnMaxes[k];
2530:                        } else {
2531:                            int portion = 0;
2532:
2533:                            if (constraints[k] < 0) {
2534:                                // Percentage
2535:                                int percentage = -constraints[k];
2536:                                // #108602 When table is auto, count the pref width (here as max).
2537:                                if (tableWidth == AUTO) {
2538:                                    portion = (percentage * columnMaxes[k]) / 100;
2539:                                } else {
2540:                                    portion = (percentage * tableWidth) / 100;
2541:                                }
2542:                            } else {
2543:                                portion = constraints[k];
2544:                            }
2545:
2546:                            if (portion > columnMins[k]) {
2547:                                max += portion;
2548:                            } else {
2549:                                max += columnMins[k];
2550:                            }
2551:                        }
2552:                    }
2553:
2554:                    if (max <= containing) {
2555:                        tableWidth = max;
2556:
2557:                        // We can fit the maximums for all columns!
2558:                        for (int k = 0; k < columns; k++) {
2559:                            if (constraints[k] == AUTO) {
2560:                                columnWidths[k] = columnMaxes[k];
2561:                            } else {
2562:                                int portion = 0;
2563:
2564:                                if (constraints[k] < 0) {
2565:                                    // Percentage
2566:                                    int percentage = -constraints[k];
2567:                                    // #108602 When table is auto, count the pref width (here as max).
2568:                                    if (tableWidth == AUTO) {
2569:                                        portion = (percentage * columnMaxes[k]) / 100;
2570:                                    } else {
2571:                                        portion = (percentage * tableWidth) / 100;
2572:                                    }
2573:                                } else {
2574:                                    portion = constraints[k];
2575:                                }
2576:
2577:                                if (portion > columnMins[k]) {
2578:                                    columnWidths[k] = portion;
2579:                                } else {
2580:                                    columnWidths[k] = columnMins[k];
2581:                                }
2582:                            }
2583:                        }
2584:                    } else {
2585:                        int min = 0;
2586:
2587:                        for (int k = 0; k < columns; k++) {
2588:                            min += columnMins[k];
2589:                        }
2590:
2591:                        if (min >= containing) {
2592:                            tableWidth = min;
2593:
2594:                            // Use minimum cells
2595:                            for (int k = 0; k < columns; k++) {
2596:                                columnWidths[k] = columnMins[k];
2597:                            }
2598:                        } else {
2599:                            // XXX: should we add cell spacing etc. here?
2600:                            tableWidth = containing;
2601:                            assignColumnWidths(tableWidth, min, columnWidths,
2602:                                    columnMins, columnMaxes, constraints);
2603:                        }
2604:                    }
2605:                }
2606:
2607:                // XXX TODO - how do we deal with this:
2608:                // A percentage value for a column width is relative to the
2609:                // table width. If the table has 'width: auto', a percentage
2610:                // represents a constraint on the column's width, which a UA
2611:                // should try to satisfy. (Obviously, this is not always
2612:                // possible: if the column's width is '110%', the constraint
2613:                // cannot be satisfied.)
2614:            }
2615:
2616:            private void distribute(int[] lengths, int leftOver, int count) {
2617:                // Spread remainder out evenly
2618:                int portion = leftOver / count;
2619:                int remainder = leftOver % count;
2620:
2621:                for (int i = 0; i < count; i++) {
2622:                    lengths[i] = portion;
2623:                }
2624:
2625:                lengths[0] += remainder;
2626:            }
2627:
2628:            /**
2629:             * Distribute the space over the columns in the case where
2630:             * the table width is larger than the minimum required
2631:             * by the minimum cells, but less than the width required
2632:             * by the maximum/preferred cell widths.
2633:             * @todo Consider fixed widths and don't add widths to these!!
2634:             */
2635:            private void assignColumnWidths(int tableWidth, int min,
2636:                    int[] columnWidths, int[] columnMins, int[] columnMaxes,
2637:                    int[] constraints) {
2638:                /*
2639:                 * Turns out Mozilla does NOT do proportional assignment - and in fact the
2640:                 * Braveheart stylesheets rely on this. For example, they want the sorting
2641:                 * buttons pushed over on the right, so they simply set the cell width to 100%
2642:                 * with the net result that the second cell, the button gets assigned its
2643:                 * minimum width because it appears later.
2644:                 *
2645:                 * So I'll do that too...
2646:                 *
2647:                 */
2648:
2649:                // Now assign the available width
2650:                // Distribute the available space.
2651:                // First assign all the minimum widths to the columns.
2652:                // Then dole out the percentage columns space.
2653:                // Finally hand out the remaining space proportionally
2654:                // may be no such columns.  If so we will ignore the
2655:                // requested height on the table.
2656:                // Initially, assign minimum widths
2657:                for (int k = 0; k < columns; k++) {
2658:                    columnWidths[k] = columnMins[k];
2659:                }
2660:
2661:                int leftOver = tableWidth - min;
2662:                int assigned = 0;
2663:
2664:                // Assign length-constrained columns
2665:                for (int i = 0; i < columns; i++) {
2666:                    int constraint = constraints[i];
2667:
2668:                    if (constraint == AUTO) {
2669:                        continue;
2670:                    }
2671:
2672:                    if (constraint >= 0) {
2673:                        int portion = constraint;
2674:                        int original = columnWidths[i];
2675:
2676:                        if (portion < original) {
2677:                            // Don't assign less than the computed minimum width for
2678:                            // this table
2679:                            continue;
2680:                        }
2681:
2682:                        // leftOver contains the amount of space we have left over
2683:                        // after assigning widths to all columns. That includes the
2684:                        // width already assigned to this column being considered.
2685:                        // We add that back in now since we'll be replacing the
2686:                        // assigned width, not add to it.
2687:                        int left = leftOver + columnWidths[i];
2688:
2689:                        if (portion > left) {
2690:                            portion = left;
2691:                        }
2692:
2693:                        columnWidths[i] = portion;
2694:                        assigned += (portion - original);
2695:                        leftOver = left - portion;
2696:
2697:                        if (leftOver <= 0) {
2698:                            break;
2699:                        }
2700:                    }
2701:                }
2702:
2703:                // Assign percentage-constrained columns
2704:                for (int i = 0; i < columns; i++) {
2705:                    if (constraints[i] == AUTO) {
2706:                        continue;
2707:                    }
2708:
2709:                    if (constraints[i] < 0) {
2710:                        int original = columnWidths[i];
2711:                        int percent = -constraints[i];
2712:                        int portion = (percent * tableWidth) / 100;
2713:
2714:                        if (portion < original) {
2715:                            // Don't assign less than the computed minimum width for
2716:                            // this table
2717:                            continue;
2718:                        }
2719:
2720:                        // leftOver contains the amount of space we have left over
2721:                        // after assigning widths to all columns. That includes the
2722:                        // width already assigned to this column being considered.
2723:                        // We add that back in now since we'll be replacing the
2724:                        // assigned width, not add to it.
2725:                        int left = leftOver + columnWidths[i];
2726:
2727:                        if (portion > left) {
2728:                            portion = left;
2729:                        }
2730:
2731:                        columnWidths[i] = portion;
2732:                        assigned += (portion - original);
2733:                        leftOver = left - portion;
2734:
2735:                        if (leftOver <= 0) {
2736:                            break;
2737:                        }
2738:                    }
2739:                }
2740:
2741:                if (leftOver <= 0) {
2742:                    return;
2743:                }
2744:
2745:                // I have extra space to be distributed among unconstrained columns.
2746:                // If there are no unconstrained columns, distribute it to the percentage
2747:                // columns, and if there are none of those, distribute it evenly.
2748:                int unconstrained = 0;
2749:                int percentages = 0;
2750:
2751:                for (int i = 0; i < columns; i++) {
2752:                    if (constraints[i] == AUTO) {
2753:                        unconstrained++;
2754:                    } else if (constraints[i] < 0) {
2755:                        percentages++;
2756:                    }
2757:                }
2758:
2759:                if (unconstrained == 0) {
2760:                    // TODO -- do proportional assignment here rather than just distributing
2761:                    // evenly among the columns??
2762:                    // No unconstrained columns            
2763:                    if (percentages == 0) {
2764:                        // No percentage columns: distribute space proportionally
2765:                        // among all columns
2766:                        int portion = leftOver / columns;
2767:                        int remainder = leftOver % columns;
2768:
2769:                        for (int i = 0; i < columns; i++) {
2770:                            columnWidths[i] += portion;
2771:                        }
2772:
2773:                        columnWidths[0] += remainder;
2774:                    } else {
2775:                        // Assign the extra space to the percentage columns
2776:                        int portion = leftOver / percentages;
2777:                        int remainder = leftOver % percentages;
2778:
2779:                        for (int i = 0; i < columns; i++) {
2780:                            if ((constraints[i] != AUTO)
2781:                                    && (constraints[i] < 0)) {
2782:                                columnWidths[i] += portion;
2783:                                columnWidths[i] += remainder;
2784:                                remainder = 0;
2785:                            }
2786:                        }
2787:                    }
2788:
2789:                    return;
2790:                }
2791:
2792:                // Distribute the remaining space among the unconstrained columns. This is
2793:                // distributed based on the columnMins and columnMaxes arrays, which states
2794:                // requirements (mins) and desires (maxes) from the cell contents.
2795:                while (true) {
2796:                    // Find the total for the cells that still allow more space,
2797:                    // so we can divy up the available space proportionally
2798:                    int colSum = 0;
2799:                    int count = 0;
2800:
2801:                    for (int i = 0; i < columns; i++) {
2802:                        if (constraints[i] != AUTO) {
2803:                            continue;
2804:                        }
2805:
2806:                        if (columnWidths[i] < columnMaxes[i]) {
2807:                            colSum += columnWidths[i];
2808:                            count++;
2809:                        }
2810:                    }
2811:
2812:                    if ((colSum == 0) && (count != 0)) {
2813:                        // We have some columns that have been assigned zero width
2814:                        // that still allow some width. Since they have zero width
2815:                        // we can't do a proportional assignment, we'll just distribute
2816:                        // it evenly. This is an unusual scenario, but can happen when
2817:                        // you for example have a table with colspans to make the table
2818:                        // wider but nothing actually filling it.
2819:                        int portion = leftOver / count;
2820:                        int remainder = leftOver % count;
2821:
2822:                        for (int i = 0; i < columns; i++) {
2823:                            if (constraints[i] != AUTO) {
2824:                                continue;
2825:                            }
2826:
2827:                            if (columnWidths[i] < columnMaxes[i]) {
2828:                                // XXX This is not right!!!
2829:                                // I should consider columnMaxes and fixedWidths!
2830:                                // Oooh, since colSum was 0, I know they don't have
2831:                                // fixedwidth!!! (unless it was zero, but that would
2832:                                // be weird)
2833:                                columnWidths[i] += portion;
2834:
2835:                                if (remainder != 0) {
2836:                                    columnWidths[i] += remainder;
2837:                                    remainder = 0;
2838:                                }
2839:                            }
2840:                        }
2841:
2842:                        return;
2843:                    }
2844:
2845:                    assigned = 0;
2846:
2847:                    int maxedPixels = 0; // Amount of pixels we've handed back to the pool
2848:
2849:                    for (int i = 0; i < columns; i++) {
2850:                        if (constraints[i] != AUTO) {
2851:                            continue;
2852:                        }
2853:
2854:                        if (columnWidths[i] >= columnMaxes[i]) {
2855:                            continue;
2856:                        }
2857:
2858:                        int portion = (columnWidths[i] * leftOver) / colSum;
2859:                        columnWidths[i] += portion;
2860:
2861:                        if (columnWidths[i] >= columnMaxes[i]) {
2862:                            maxedPixels += (columnWidths[i] - columnMaxes[i]);
2863:                            columnWidths[i] = columnMaxes[i];
2864:                        } else {
2865:                            assigned += portion;
2866:                        }
2867:                    }
2868:
2869:                    // remainder from rounding errors
2870:                    // TODO fix this - computation isn't right, and I shouldn't
2871:                    // arbitrarily assign column 0, I should assign to one
2872:                    // of the columns above that was incremented
2873:                    //columnWidths[0] += leftOver-assigned-maxedPixels;
2874:                    if (maxedPixels == 0) {
2875:                        break;
2876:                    }
2877:
2878:                    leftOver = maxedPixels;
2879:                }
2880:
2881:                // TODO - if we have rounding errors, column widths may not add
2882:                // up - should we correct that here by bumping up one of the columns?
2883:            }
2884:
2885:            public boolean isBorderSizeIncluded() {
2886:                return true;
2887:            }
2888:
2889:            public Insets getCssSizeInsets() {
2890:                // Tables include their own borders so they should not be included here; however,
2891:                // we have to account for the caption
2892:                if (captionBoxes != null) {
2893:                    return new Insets(tableTop, tableLeft,
2894:                            height - tableBottom, width - tableRight);
2895:                } else {
2896:                    return new Insets(0, 0, 0, 0);
2897:                }
2898:            }
2899:
2900:            public int getPrefWidth() {
2901:                // See comment under getPrefMinWidth; I should be able to do
2902:                // something better here. XXX Optimize: return the same computation.
2903:                try {
2904:                    computingPrefWidth = true;
2905:
2906:                    return getPrefMinWidth();
2907:                } finally {
2908:                    computingPrefWidth = false;
2909:                }
2910:            }
2911:
2912:            public int getPrefMinWidth() {
2913:                if ((columns == 0) || (rows == 0)) {
2914:                    //            int tableWidth = CssLookup.getLength(table, XhtmlCss.WIDTH_INDEX);
2915:                    int tableWidth = CssUtilities.getCssLength(table,
2916:                            XhtmlCss.WIDTH_INDEX);
2917:                    //            CssLookup.uncompute(table, XhtmlCss.WIDTH_INDEX);
2918:                    CssProvider.getEngineService().uncomputeValueForElement(
2919:                            table, XhtmlCss.WIDTH_INDEX);
2920:
2921:                    if (tableWidth != AUTO) {
2922:                        return tableWidth;
2923:                    } else {
2924:                        return 0;
2925:                    }
2926:                }
2927:
2928:                /*
2929:                  I can't just return getTableWidth() because if the sum of the
2930:                  minimum cell widths is greater than the table width, we need
2931:                  to use the cell width minimum sum.
2932:                int tableWidth = getTableWidth();
2933:                if (tableWidth != AUTO) {
2934:                    // XXX Hm, this isn't really true. If the table cells themselves
2935:                    // don't fit in the allocated width, we enlarge the table.
2936:                    // Thus, I might not be able to do the below computation.
2937:                    return tableWidth;
2938:                }
2939:                 */
2940:
2941:                // TODO I can probably do faster than this, e.g. call the cells
2942:                // column by column, calling getPrefWidth on each and taking the
2943:                // max (and accounting for table cell widths, colspan/rowspan>1,
2944:                // etc.)  Also, I want to make sure I truly pick up the maximum
2945:                // widths, not something constrained by my
2946:                // 0-containingBlockWidth (which is set to 0 to force
2947:                // percentages to compute to 0, ... if not I'd like to set it to
2948:                // something really large.)
2949:                int[] columnWidths = new int[columns];
2950:
2951:                if (fixedLayout) {
2952:                    computeFixedColumnWidths(columnWidths, true);
2953:                } else {
2954:                    // Passing in null formatting context: we assume that the
2955:                    // parent calling getPrefWidth will already have called
2956:                    // initializeHorizontalWidths recursively down the box tree,
2957:                    // so our columns have already been initialized.
2958:                    computeAutoColumnWidths(columnWidths, null, true);
2959:                }
2960:
2961:                int w = 0;
2962:
2963:                for (int m = 0; m < columns; m++) {
2964:                    w += columnWidths[m];
2965:                }
2966:
2967:                // XXX is this right?
2968:                w += (leftPadding + rightPadding);
2969:
2970:                if (leftMargin != AUTO) {
2971:                    w += leftMargin;
2972:                }
2973:
2974:                if (rightMargin != AUTO) {
2975:                    w += rightMargin;
2976:                }
2977:
2978:                // Note: we don't add borderwidths and padding to the result
2979:                // since as a special case, tables already include them in 
2980:                // their specified width property
2981:                // XXX That doesn't seem right!  AHA! Perhaps I need to consider whether WIDTH was set or not!!!
2982:                w += (leftBorderWidth + rightBorderWidth);
2983:
2984:                return w;
2985:            }
2986:
2987:            /** Compute the height of the table. May return AUTO.
2988:             */
2989:            private int getTableHeight() {
2990:                //        Value val = CssLookup.getValue(table, XhtmlCss.HEIGHT_INDEX);
2991:                CssValue cssValue = CssProvider.getEngineService()
2992:                        .getComputedValueForElement(table,
2993:                                XhtmlCss.HEIGHT_INDEX);
2994:
2995:                //        if (val == CssValueConstants.AUTO_VALUE) {
2996:                if (CssProvider.getValueService().isAutoValue(cssValue)) {
2997:                    return AUTO;
2998:                } else {
2999:                    // Percentages in table heights don't apply!
3000:                    //            boolean wasPercentage =
3001:                    //                val instanceof ComputedValue &&
3002:                    //                (((ComputedValue)val).getCascadedValue().getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE);
3003:                    boolean wasPercentage = cssValue instanceof  CssComputedValue
3004:                            && CssProvider.getValueService()
3005:                                    .isOfPrimitivePercentageType(
3006:                                            ((CssComputedValue) cssValue)
3007:                                                    .getCascadedValue());
3008:
3009:                    if (wasPercentage) {
3010:                        return 0;
3011:                    }
3012:
3013:                    //            int tableHeight = (int)val.getFloatValue();
3014:                    int tableHeight = (int) cssValue.getFloatValue();
3015:
3016:                    // Empirically I've noticed mozilla treats tables differently; 
3017:                    // than regular boxes: the width: property affects the final
3018:                    // width of the table (including borders and padding).
3019:                    tableHeight -= (topBorderWidth + topPadding + bottomPadding + bottomBorderWidth);
3020:
3021:                    // Each of the cells will absorb the space for the top and left
3022:                    // padding area. However, we also need to have a padding area
3023:                    // on the right side of the rightmost column, so account for
3024:                    // that space here.
3025:                    tableHeight -= cellSpacing;
3026:
3027:                    return tableHeight;
3028:                }
3029:            }
3030:
3031:            /** {@inheritDoc}
3032:             * Overridden so I can add in the table position, which may not be 0,0 because
3033:             * we may have a caption
3034:             * @todo How is the clip area affected by this?
3035:             */
3036:            protected void paintBackground(Graphics g, int x, int y) {
3037:                if (hidden) {
3038:                    return;
3039:                }
3040:
3041:                if (captionBoxes != null) {
3042:                    paintBox(g, x + tableLeft, y + tableTop, tableRight
3043:                            - tableLeft, tableBottom - tableTop);
3044:                } else {
3045:                    paintBox(g, x, y, getWidth(), getHeight());
3046:                }
3047:            }
3048:
3049:            public int getRows() {
3050:                return rows;
3051:            }
3052:
3053:            public int getColumns() {
3054:                return columns;
3055:            }
3056:
3057:            public CssBox getCell(int row, int col) {
3058:                return cells[row][col];
3059:            }
3060:
3061:            public int getCellSpan(int axis, int row, int col) {
3062:                return (axis == CssBox.X_AXIS) ? rowspans[row][col]
3063:                        : colspans[row][col];
3064:            }
3065:
3066:            /* TODO -- implement for table too, where we have a large cellspacing.
3067:               Users may be trying to resize in the cell space areas where the
3068:               cells themselves are not active...
3069:            public int getInternalResizeDirection(int x, int y) {
3070:                if (tableDesignInfo == null) {
3071:                    return Cursor.DEFAULT_CURSOR;
3072:                }
3073:
3074:                int left = getAbsoluteX();
3075:
3076:                // See if it looks like this component is near the computed border
3077:                // of any of our boxes
3078:
3079:                // First find our row
3080:                int cellY = getAbsoluteY();
3081:                int row = 0;
3082:                for (; row < rows; row++) {
3083:                    int next = cellY + rowHeights[row];
3084:                    if (next > y) {
3085:                        break;
3086:                    }
3087:                    cellY = next;
3088:                }
3089:                if (row == rows) {
3090:                    return Cursor.DEFAULT_CURSOR;
3091:                }
3092:                int top = cellY;
3093:                int height = rowHeights[row];
3094:                return Cursor.DEFAULT_CURSOR;
3095:            }
3096:             */
3097:
3098:            /** Cells are just like regular css container boxes, except
3099:             * they have special margin and padding handling */
3100:            private static class CellBox extends ContainerBox {
3101:                private TableBox tableBox;
3102:                int row;
3103:                int col;
3104:                int originalHeight; // before forcing height to match the table grid
3105:
3106:                /** Construct a new CellBox, with the given default
3107:                 * border width to use if the element does not have a
3108:                 * specific CSS border-related property. Ditto for padding. */
3109:                public CellBox(WebForm webform, Element element,
3110:                        BoxType boxType, boolean inline, TableBox tableBox) {
3111:                    super (webform, element, boxType, inline, false);
3112:                    this .tableBox = tableBox;
3113:                    initializeBackgroundDelayed(); // see comment in initializeBackground()
3114:                }
3115:
3116:                public Decoration getDecoration() {
3117:                    // XXX No decorations for cells.
3118:                    return null;
3119:                }
3120:
3121:                protected void initializeMargins() {
3122:                    // Margins are not allowed on <td>'s
3123:                    // TODO find reference for that here, I believe I read it somewhere
3124:                    // and it certainly matches what Mozilla seems to do
3125:                    leftMargin = rightMargin = topMargin = bottomMargin = 0;
3126:                    effectiveTopMargin = effectiveBottomMargin = 0;
3127:                }
3128:
3129:                protected void initializePadding() {
3130:                    // Padding: mozilla seems to allow padding properties to be set
3131:                    // on individual cells - and if so, it takes the larger of
3132:                    // the default cell padding provided by the table and the
3133:                    // per cell padding
3134:                    leftPadding = rightPadding = AUTO;
3135:                    topPadding = bottomPadding = AUTO;
3136:
3137:                    super .initializePadding();
3138:
3139:                    int cellPadding = tableBox.cellPadding;
3140:
3141:                    if (leftPadding == AUTO) {
3142:                        leftPadding = cellPadding;
3143:                    } else {
3144:                        leftPadding = Math.max(leftPadding, cellPadding);
3145:                    }
3146:
3147:                    if (rightPadding == AUTO) {
3148:                        rightPadding = cellPadding;
3149:                    } else {
3150:                        rightPadding = Math.max(rightPadding, cellPadding);
3151:                    }
3152:
3153:                    if (topPadding == AUTO) {
3154:                        topPadding = cellPadding;
3155:                    } else {
3156:                        topPadding = Math.max(topPadding, cellPadding);
3157:                    }
3158:
3159:                    if (bottomPadding == AUTO) {
3160:                        bottomPadding = cellPadding;
3161:                    } else {
3162:                        bottomPadding = Math.max(bottomPadding, cellPadding);
3163:                    }
3164:                }
3165:
3166:                protected void initializeBorder() {
3167:                    int cellBorderWidth = -1;
3168:                    int style = CssBorder.STYLE_NONE;
3169:                    int rules = tableBox.rules;
3170:
3171:                    if (tableBox.borderWidth > 0) {
3172:                        // If the table has a nonzero border width attribute, ensure that
3173:                        // we pick up the following default style (inset border width 1)
3174:                        // if more specific CSS rules haven't been set for a particular cell
3175:                        cellBorderWidth = 1;
3176:                        style = CssBorder.STYLE_INSET;
3177:                    } else if (tableBox.rules != CssBorder.FRAME_UNSET) {
3178:                        // If we have some rules applied we need to draw the internal cell
3179:                        // structure. Arguably I should mask out the external border sides here.
3180:                        cellBorderWidth = 1;
3181:
3182:                        // Don't show cell borders on the edges of the table; "rules" applies
3183:                        // only to the internal lines (collapsing model).
3184:                        if (row == 0) {
3185:                            rules = rules & ~CssBorder.FRAME_TOP;
3186:                        }
3187:
3188:                        if ((row + tableBox.colspans[row][col]) == tableBox.rows) {
3189:                            rules = rules & ~CssBorder.FRAME_BOTTOM;
3190:                        }
3191:
3192:                        if (col == 0) {
3193:                            rules = rules & ~CssBorder.FRAME_LEFT;
3194:                        }
3195:
3196:                        if ((col + tableBox.colspans[row][col]) == tableBox.columns) {
3197:                            rules = rules & ~CssBorder.FRAME_RIGHT;
3198:                        }
3199:
3200:                        style = CssBorder.STYLE_SOLID;
3201:                    }
3202:
3203:                    border = CssBorder.getBorder(getElement(), cellBorderWidth,
3204:                            style, rules);
3205:
3206:                    if (border != null) {
3207:                        leftBorderWidth = border.getLeftBorderWidth();
3208:                        topBorderWidth = border.getTopBorderWidth();
3209:                        bottomBorderWidth = border.getBottomBorderWidth();
3210:                        rightBorderWidth = border.getRightBorderWidth();
3211:                    }
3212:
3213:                    // NO design border here!
3214:                    //considerDesignBorder();
3215:                }
3216:
3217:                /**
3218:                    Compute the background color to use for a cell.
3219:                    The order is specified in the CSS2.1 spec, section 17.5.1:
3220:                    <ul>
3221:                    <li> Cells
3222:                    <li> Rows
3223:                    <li> Row Groups
3224:                    <li> Columns
3225:                    <li> Column Groups
3226:                    <li> Table
3227:                    </ul>
3228:                    <p>
3229:                XXX TODO: Do this via stylesheet inheritance instead:
3230:                <pre>
3231:                td, th, tr {
3232:                    background: inherit;
3233:                    background-color: inherit;
3234:                }
3235:                </pre>
3236:                Of course, that will make it difficult to get the exact right
3237:                color given the table layers specified in 17.5.1.
3238:                 */
3239:                protected void initializeBackground() {
3240:                    // Not calling super
3241:                    // And in fact doing nothing -- that's because this shouldn't be called 
3242:                    // during initializeInvariants() since at that point, our own constructor
3243:                    // hasn't been called yet (only the super-constructor in CssBox which calls
3244:                    // initializeInvariants) and we need to wait until we've initialized
3245:                    // the tableBox field in our own constructor, so initializeBackground
3246:                    // does nothing, and in our own constructor we call initializeBackgroundDelayed instead
3247:                }
3248:
3249:                protected void initializeBackgroundDelayed() {
3250:                    initializeBackgroundImage();
3251:
3252:                    Element element = getElement();
3253:                    //            bg = CssLookup.getColor(element, XhtmlCss.BACKGROUND_COLOR_INDEX);
3254:                    bg = CssProvider.getValueService().getColorForElement(
3255:                            element, XhtmlCss.BACKGROUND_COLOR_INDEX);
3256:
3257:                    if (bg != null) {
3258:                        return;
3259:                    }
3260:
3261:                    // Check row
3262:                    //            Element row = findParent(element, CssValueConstants.TABLE_ROW_VALUE);
3263:                    Element row = findParent(element, CssProvider
3264:                            .getValueService().getTableRowValueConstant());
3265:
3266:                    if (row == null) {
3267:                        return;
3268:                    }
3269:
3270:                    //            bg = CssLookup.getColor(row, XhtmlCss.BACKGROUND_COLOR_INDEX);
3271:                    bg = CssProvider.getValueService().getColorForElement(row,
3272:                            XhtmlCss.BACKGROUND_COLOR_INDEX);
3273:
3274:                    if (bg != null) {
3275:                        return;
3276:                    }
3277:
3278:                    // Check row group:
3279:                    // Check columns:
3280:                    // Check column groups:
3281:                    // TODO - not keeping column/columngroup/rowgroup information
3282:                    // for cells!
3283:                    // tbody/tfoot/thead
3284:                    //            Element section = findParent(row, CssValueConstants.TABLE_ROW_GROUP_VALUE);
3285:                    Element section = findParent(row, CssProvider
3286:                            .getValueService().getTableRowGroupValueConstant());
3287:
3288:                    if (section == null) {
3289:                        //                section = findParent(row, CssValueConstants.TABLE_HEADER_GROUP_VALUE);
3290:                        section = findParent(row, CssProvider.getValueService()
3291:                                .getTableHeaderGroupValueConstant());
3292:
3293:                        if (section == null) {
3294:                            //                    section = findParent(row, CssValueConstants.TABLE_FOOTER_GROUP_VALUE);
3295:                            section = findParent(row, CssProvider
3296:                                    .getValueService()
3297:                                    .getTableFooterGroupValueConstant());
3298:
3299:                            if (section == null) {
3300:                                // The element is not within a tbody, thead or tfoot;
3301:                                // it may be within an "implied" tbody so use that.
3302:                                //                        if (tableBox.table instanceof RaveTableElement) {
3303:                                //                            section = ((RaveTableElement)tableBox.table).getTbody();
3304:                                //                        }
3305:                                section = MarkupService
3306:                                        .getTBodyElementForTableElement(tableBox.table);
3307:
3308:                                if (section == null) {
3309:                                    return;
3310:                                }
3311:                            }
3312:                        }
3313:                    }
3314:
3315:                    //            bg = CssLookup.getColor(section, XhtmlCss.BACKGROUND_COLOR_INDEX);
3316:                    bg = CssProvider.getValueService().getColorForElement(
3317:                            section, XhtmlCss.BACKGROUND_COLOR_INDEX);
3318:
3319:                    if (bg != null) {
3320:                        return;
3321:                    }
3322:
3323:                    // Check table:
3324:                    // Not necessary. The table will paint its own background.
3325:                    // TODO: Improve performance here.
3326:                    // If none of the cells specify a background other than the
3327:                    // table, have the table do its own background. If more than
3328:                    // half of the cells specify and alternative background,
3329:                    // let ALL the cells do their own painting (by setting the
3330:                    // remaining cells to the parent bg color).
3331:                }
3332:
3333:                public boolean isBorderSizeIncluded() {
3334:                    return false;
3335:                }
3336:
3337:                public Insets getCssSizeInsets() {
3338:                    return new Insets(0, 0, 0, 0);
3339:                }
3340:
3341:                // Same as in parent ContainerBox, but does NOT include call to super
3342:                // which looks at the content width of the box itself; that's computed
3343:                // from the constraints. In other words, if you set a width of 500px on
3344:                // a <td> that should not result in 500 being passed in a a minimum and
3345:                // possibly maximum column width in the width algorithm.
3346:                public int getPrefMinWidth() {
3347:                    if (inline) {
3348:                        //                Value val = CssLookup.getValue(getElement(), XhtmlCss.WHITE_SPACE_INDEX);
3349:                        CssValue cssValue = CssProvider.getEngineService()
3350:                                .getComputedValueForElement(getElement(),
3351:                                        XhtmlCss.WHITE_SPACE_INDEX);
3352:
3353:                        //                if ((val == CssValueConstants.PRE_VALUE) ||
3354:                        //                        (val == CssValueConstants.NOWRAP_VALUE)) {
3355:                        if (CssProvider.getValueService().isPreValue(cssValue)
3356:                                || CssProvider.getValueService().isNoWrapValue(
3357:                                        cssValue)) {
3358:                            return getPrefWidth();
3359:                        }
3360:                    }
3361:
3362:                    int largest = 0;
3363:                    int n = getBoxCount();
3364:
3365:                    for (int i = 0; i < n; i++) {
3366:                        CssBox child = getBox(i);
3367:
3368:                        if (child.getBoxType().isAbsolutelyPositioned()) {
3369:                            continue;
3370:                        }
3371:
3372:                        int min = child.getPrefMinWidth();
3373:
3374:                        if (min > largest) {
3375:                            largest = min;
3376:                        }
3377:                    }
3378:
3379:                    if (leftMargin != AUTO) {
3380:                        largest += leftMargin;
3381:                    }
3382:
3383:                    if (rightMargin != AUTO) {
3384:                        largest += rightMargin;
3385:                    }
3386:
3387:                    // Borders and padding can't be left auto, can they?
3388:                    largest += (leftBorderWidth + leftPadding
3389:                            + rightBorderWidth + rightPadding);
3390:
3391:                    // Don't call super, but do compute its borders and margins just in case
3392:                    //int curr = super.getPrefMinWidth();
3393:                    int curr = (leftBorderWidth + leftPadding + rightPadding + rightBorderWidth);
3394:
3395:                    if (leftMargin != AUTO) {
3396:                        curr += leftMargin;
3397:                    }
3398:
3399:                    if (rightMargin != AUTO) {
3400:                        curr += rightMargin;
3401:                    }
3402:
3403:                    if (curr > largest) {
3404:                        largest = curr;
3405:                    }
3406:
3407:                    return largest;
3408:                }
3409:
3410:                public int getPrefWidth() {
3411:                    int largest = 0;
3412:                    int n = getBoxCount();
3413:
3414:                    if (inline && !boxType.isAbsolutelyPositioned()) {
3415:                        // Let the line box compute the size of these children
3416:                        CssBox curr = getParent();
3417:
3418:                        while ((curr != null)
3419:                                && !(curr instanceof  LineBoxGroup)) {
3420:                            curr = curr.getParent();
3421:                        }
3422:
3423:                        if (curr != null) {
3424:                            largest = ((LineBoxGroup) curr).getPrefWidth(boxes);
3425:                        } else {
3426:                            // Shouldn't happen - this is just for safety right now
3427:                            // Inline tag: add up all the children and use that
3428:                            for (int i = 0; i < n; i++) {
3429:                                CssBox child = getBox(i);
3430:
3431:                                if (child.getBoxType().isAbsolutelyPositioned()) {
3432:                                    continue;
3433:                                }
3434:
3435:                                // XXX does not properly handle LineBox.LINEBREAK
3436:                                largest += child.getPrefWidth();
3437:                            }
3438:                        }
3439:                    } else {
3440:                        // Block tag: find the widest child and use that
3441:                        for (int i = 0; i < n; i++) {
3442:                            CssBox child = getBox(i);
3443:
3444:                            if (child.getBoxType().isAbsolutelyPositioned()) {
3445:                                continue;
3446:                            }
3447:
3448:                            int min = child.getPrefWidth();
3449:
3450:                            if (min > largest) {
3451:                                largest = min;
3452:                            }
3453:                        }
3454:                    }
3455:
3456:                    if (leftMargin != AUTO) {
3457:                        largest += leftMargin;
3458:                    }
3459:
3460:                    if (rightMargin != AUTO) {
3461:                        largest += rightMargin;
3462:                    }
3463:
3464:                    // Borders and padding can't be left auto, can they?
3465:                    largest += (leftBorderWidth + leftPadding
3466:                            + rightBorderWidth + rightPadding);
3467:
3468:                    // Don't call super, but do compute its borders and margins just in case
3469:                    //int curr = super.getPrefWidth();
3470:                    int curr = (leftBorderWidth + leftPadding + rightPadding + rightBorderWidth);
3471:
3472:                    if (leftMargin != AUTO) {
3473:                        curr += leftMargin;
3474:                    }
3475:
3476:                    if (rightMargin != AUTO) {
3477:                        curr += rightMargin;
3478:                    }
3479:
3480:                    if (curr > largest) {
3481:                        largest = curr;
3482:                    }
3483:
3484:                    return largest;
3485:                }
3486:
3487:                /** Locate a particular type of parent within the table. */
3488:                private Element findParent(Element start, CssValue cssValue) {
3489:                    while (start != null) {
3490:                        // We can cast since we should be within a <table>
3491:                        // element
3492:                        Node parent = start.getParentNode();
3493:
3494:                        if (parent.getNodeType() != Node.ELEMENT_NODE) {
3495:                            return null;
3496:                        }
3497:
3498:                        start = (Element) parent;
3499:
3500:                        if ((start == null) || (start == tableBox.table)) {
3501:                            return null;
3502:                        }
3503:
3504:                        //                if (CssLookup.getValue(start, XhtmlCss.DISPLAY_INDEX) == value) {
3505:                        if (cssValue.equals(CssProvider.getEngineService()
3506:                                .getComputedValueForElement(start,
3507:                                        XhtmlCss.DISPLAY_INDEX))) {
3508:                            return start;
3509:                        }
3510:                    }
3511:
3512:                    return null;
3513:                }
3514:
3515:                /** Align the cell contents within the cell. This method is called
3516:                 * when the box height and contentHeight have already been initialized
3517:                 * to their final values, and the cell contents have also been
3518:                 * laid out.
3519:                 */
3520:                void align() {
3521:                    //            Value align = CssLookup.getValue(getElement(), XhtmlCss.VERTICAL_ALIGN_INDEX);
3522:                    CssValue cssAlign = CssProvider.getEngineService()
3523:                            .getComputedValueForElement(getElement(),
3524:                                    XhtmlCss.VERTICAL_ALIGN_INDEX);
3525:
3526:                    //            if ((align == CssValueConstants.BASELINE_VALUE) ||
3527:                    //                    (align == CssValueConstants.SUB_VALUE) || // sub==baseline in tables
3528:                    //                    (align == CssValueConstants.SUPER_VALUE) || // ditto (see 17.5.3)
3529:                    //                    (align == CssValueConstants.TEXT_TOP_VALUE) || // ditto
3530:                    //                    (align == CssValueConstants.TEXT_BOTTOM_VALUE)) { // ditto
3531:                    if (CssProvider.getValueService().isBaseLineValue(cssAlign)
3532:                            || CssProvider.getValueService().isSubValue(
3533:                                    cssAlign)
3534:                            || CssProvider.getValueService().isSuperValue(
3535:                                    cssAlign)
3536:                            || CssProvider.getValueService().isTextTopValue(
3537:                                    cssAlign)
3538:                            || CssProvider.getValueService().isTextTopValue(
3539:                                    cssAlign)) {
3540:
3541:                        // XXX for now: just leave it as is. This is ROUGHLY right,
3542:                        // except for the case where there are different fonts
3543:                        // in different cells; in that case smaller fonts on the
3544:                        // first line would cause the cell contents to shift down
3545:                        // to align with the baseline for the tallest line.
3546:                        //            } else if (align == CssValueConstants.TOP_VALUE) {
3547:                    } else if (CssProvider.getValueService().isTopValue(
3548:                            cssAlign)) {
3549:                        // Already done!
3550:                        //            } else if (align == CssValueConstants.BOTTOM_VALUE) {
3551:                    } else if (CssProvider.getValueService().isBottomValue(
3552:                            cssAlign)) {
3553:                        // This implements bottom, not text-bottom so add separate
3554:                        // computation for that
3555:                        int childrenHeight = originalHeight;
3556:                        int delta = height - childrenHeight;
3557:
3558:                        for (int i = 0, n = getBoxCount(); i < n; i++) {
3559:                            CssBox box = getBox(i);
3560:                            box.setY(box.getY() + delta);
3561:                        }
3562:                        //            } else if (align == CssValueConstants.MIDDLE_VALUE) {
3563:                    } else if (CssProvider.getValueService().isMiddleValue(
3564:                            cssAlign)) {
3565:                        int childrenHeight = originalHeight;
3566:                        int delta = (height - childrenHeight) / 2;
3567:
3568:                        for (int i = 0, n = getBoxCount(); i < n; i++) {
3569:                            CssBox box = getBox(i);
3570:                            box.setY(box.getY() + delta);
3571:                        }
3572:                    } else {
3573:                        //                assert false : align;
3574:                        ErrorManager.getDefault().notify(
3575:                                ErrorManager.INFORMATIONAL,
3576:                                new IllegalStateException(
3577:                                        "Unexpected alignment value, cssAlign="
3578:                                                + cssAlign)); // NOI18N
3579:                    }
3580:                }
3581:
3582:                /** When we need to relayout a table cell, the table bounds itself may
3583:                 *  need to be recomputed.
3584:                 */
3585:
3586:                /*
3587:                protected void layoutChild(CssBox box, FormatContext context,
3588:                                           boolean handleChildren) {
3589:                    assert parent instanceof TableBox;
3590:                    parent.relayout(context);
3591:                    parent.parent.notifyChildResize(parent, context);
3592:                }
3593:                 */
3594:                protected CssBox notifyChildResize(CssBox child,
3595:                        FormatContext context) {
3596:                    ContainerBox parent = getParent();
3597:                    assert parent instanceof  TableBox;
3598:                    parent.relayout(context);
3599:
3600:                    return parent.getParent()
3601:                            .notifyChildResize(parent, context);
3602:                }
3603:
3604:                public int getInternalResizeDirection(int x, int y) {
3605:                    //            if (tableBox.tableDesignInfo == null) {
3606:                    //                return Cursor.DEFAULT_CURSOR;
3607:                    //            }
3608:                    Element tableComponentRootElement = CssBox
3609:                            .getElementForComponentRootCssBox(tableBox);
3610:                    if (!webform.getDomProviderService().hasTableResizeSupport(
3611:                            tableComponentRootElement)) {
3612:                        return Cursor.DEFAULT_CURSOR;
3613:                    }
3614:
3615:                    int left = getAbsoluteX();
3616:                    int top = getAbsoluteY();
3617:
3618:                    // TODO - should I allow two-dimension resizing? e.g. if you're
3619:                    // close to BOTH a row and a column border, can you resize both
3620:                    // at he same time? I suppose that makes sense...
3621:                    // TODO - decide if borderwidth affects this computation
3622:                    int spacing = tableBox.cellSpacing / 2;
3623:
3624:                    //            MarkupDesignBean tableMarkupDesignBean = CssBox.getMarkupDesignBeanForCssBox(tableBox);
3625:                    if ((col > 0)
3626:                            && (Math.abs(x - left) < (BORDER_RESIZE_DISTANCE + spacing))) {
3627:                        // Does it matter what height we pass in here?
3628:                        // I suppose I could pass in both higher and lower values
3629:                        // than the current size to see if we effectively will be able
3630:                        // to change the size, in case minimums and maximums constrain it
3631:                        //                if (tableBox.tableDesignInfo.testResizeColumn(tableBox.getDesignBean(), row, col - 1, 50) != -1) {
3632:                        //                if (tableBox.tableDesignInfo.testResizeColumn(tableMarkupDesignBean, row, col - 1, 50) != -1) {
3633:                        if (webform.getDomProviderService().testResizeColumn(
3634:                                tableComponentRootElement, row, col - 1, 50) != -1) {
3635:                            return Cursor.W_RESIZE_CURSOR;
3636:                        }
3637:                    }
3638:
3639:                    if ((col < (tableBox.columns - 1))
3640:                            && (Math.abs(x - (left + width)) < (BORDER_RESIZE_DISTANCE + spacing))) {
3641:                        //                if (tableBox.tableDesignInfo.testResizeColumn(tableBox.getDesignBean(), row, col, 50) != -1) {
3642:                        //                if (tableBox.tableDesignInfo.testResizeColumn(tableMarkupDesignBean, row, col, 50) != -1) {
3643:                        if (webform.getDomProviderService().testResizeColumn(
3644:                                tableComponentRootElement, row, col, 50) != -1) {
3645:                            return Cursor.E_RESIZE_CURSOR;
3646:                        }
3647:                    }
3648:
3649:                    if ((row > 0)
3650:                            && (Math.abs(y - top) < (BORDER_RESIZE_DISTANCE + spacing))) {
3651:                        //                if (tableBox.tableDesignInfo.testResizeRow(tableBox.getDesignBean(), row - 1, col, 50) != -1) {
3652:                        //                if (tableBox.tableDesignInfo.testResizeRow(tableMarkupDesignBean, row - 1, col, 50) != -1) {
3653:                        if (webform.getDomProviderService().testResizeRow(
3654:                                tableComponentRootElement, row - 1, col, 50) != -1) {
3655:                            return Cursor.N_RESIZE_CURSOR;
3656:                        }
3657:                    }
3658:
3659:                    if ((row < (tableBox.rows - 1))
3660:                            && (Math.abs(y - (top + height)) < (BORDER_RESIZE_DISTANCE + spacing))) {
3661:                        //                if (tableBox.tableDesignInfo.testResizeRow(tableBox.getDesignBean(), row, col, 50) != -1) {
3662:                        //                if (tableBox.tableDesignInfo.testResizeRow(tableMarkupDesignBean, row, col, 50) != -1) {
3663:                        if (webform.getDomProviderService().testResizeRow(
3664:                                tableComponentRootElement, row, col, 50) != -1) {
3665:                            return Cursor.S_RESIZE_CURSOR;
3666:                        }
3667:                    }
3668:
3669:                    return Cursor.DEFAULT_CURSOR;
3670:                }
3671:
3672:                public Interaction getInternalResizer(int x, int y) {
3673:                    //            if (tableBox.tableDesignInfo == null) {
3674:                    //                return null;
3675:                    //            }
3676:                    Element tableComponentRootElement = CssBox
3677:                            .getElementForComponentRootCssBox(tableBox);
3678:                    if (!webform.getDomProviderService().hasTableResizeSupport(
3679:                            tableComponentRootElement)) {
3680:                        return null;
3681:                    }
3682:
3683:                    int minimum = 0;
3684:                    int maximum = Integer.MAX_VALUE;
3685:                    int left = getAbsoluteX();
3686:                    int top = getAbsoluteY();
3687:
3688:                    // TODO - should I allow two-dimension resizing? e.g. if you're
3689:                    // close to BOTH a row and a column border, can you resize both
3690:                    // at he same time? I suppose that makes sense...
3691:                    // TODO - decide if borderwidth affects this computation
3692:                    int spacing = tableBox.cellSpacing / 2;
3693:
3694:                    Element element = getElement();
3695:                    //            MarkupDesignBean tableMarkupDesignBean = CssBox.getMarkupDesignBeanForCssBox(tableBox);
3696:                    if (Math.abs(x - left) < (BORDER_RESIZE_DISTANCE + spacing)) {
3697:                        // Does it matter what height we pass in here?
3698:                        // I suppose I could pass in both higher and lower values
3699:                        // than the current size to see if we effectively will be able
3700:                        // to change the size, in case minimums and maximums constrain it
3701:                        //                if (tableBox.tableDesignInfo.testResizeColumn(tableBox.getDesignBean(), row, col - 1, 50) != -1) {
3702:                        //                if (tableBox.tableDesignInfo.testResizeColumn(tableMarkupDesignBean, row, col - 1, 50) != -1) {
3703:                        if (webform.getDomProviderService().testResizeColumn(
3704:                                tableComponentRootElement, row, col - 1, 50) != -1) {
3705:                            TableResizer tableResizer =
3706:                            //                        new TableResizer(webform, tableBox.getDesignBean(), tableBox.tableDesignInfo,
3707:                            new TableResizer(webform, /*tableMarkupDesignBean, tableBox.tableDesignInfo,*/
3708:                                    tableComponentRootElement, CssBox.Y_AXIS,
3709:                                    true, left, tableBox.getAbsoluteY(),
3710:                                    getWidth(), tableBox.getHeight(), minimum,
3711:                                    maximum, row, col, element);
3712:
3713:                            return tableResizer;
3714:                        }
3715:                    }
3716:
3717:                    if (Math.abs(x - (left + width)) < (BORDER_RESIZE_DISTANCE + spacing)) {
3718:                        //                if (tableBox.tableDesignInfo.testResizeColumn(tableBox.getDesignBean(), row, col, 50) != -1) {
3719:                        //                if (tableBox.tableDesignInfo.testResizeColumn(tableMarkupDesignBean, row, col, 50) != -1) {
3720:                        if (webform.getDomProviderService().testResizeColumn(
3721:                                tableComponentRootElement, row, col, 50) != -1) {
3722:                            TableResizer tableResizer =
3723:                            //                        new TableResizer(webform, tableBox.getDesignBean(), tableBox.tableDesignInfo,
3724:                            new TableResizer(webform, /*tableMarkupDesignBean, tableBox.tableDesignInfo,*/
3725:                                    tableComponentRootElement, CssBox.Y_AXIS,
3726:                                    false, left, tableBox.getAbsoluteY(),
3727:                                    getWidth(), tableBox.getHeight(), minimum,
3728:                                    maximum, row, col, element);
3729:
3730:                            return tableResizer;
3731:                        }
3732:                    }
3733:
3734:                    if (Math.abs(y - top) < (BORDER_RESIZE_DISTANCE + spacing)) {
3735:                        //                if (tableBox.tableDesignInfo.testResizeRow(tableBox.getDesignBean(), row - 1, col, 50) != -1) {
3736:                        //                if (tableBox.tableDesignInfo.testResizeRow(tableMarkupDesignBean, row - 1, col, 50) != -1) {
3737:                        if (webform.getDomProviderService().testResizeRow(
3738:                                tableComponentRootElement, row - 1, col, 50) != -1) {
3739:                            System.out
3740:                                    .println("Probably should use row before here!");
3741:
3742:                            TableResizer tableResizer =
3743:                            //                        new TableResizer(webform, tableBox.getDesignBean(), tableBox.tableDesignInfo,
3744:                            new TableResizer(webform, /*tableMarkupDesignBean, tableBox.tableDesignInfo,*/
3745:                                    tableComponentRootElement, CssBox.X_AXIS,
3746:                                    true, tableBox.getAbsoluteX(), top,
3747:                                    getHeight(), tableBox.getWidth(), minimum,
3748:                                    maximum, row, col, element);
3749:
3750:                            return tableResizer;
3751:                        }
3752:                    }
3753:
3754:                    if (Math.abs(y - (top + height)) < (BORDER_RESIZE_DISTANCE + spacing)) {
3755:                        //                if (tableBox.tableDesignInfo.testResizeRow(tableBox.getDesignBean(), row, col, 50) != -1) {
3756:                        //                if (tableBox.tableDesignInfo.testResizeRow(tableMarkupDesignBean, row, col, 50) != -1) {
3757:                        if (webform.getDomProviderService().testResizeRow(
3758:                                tableComponentRootElement, row, col, 50) != -1) {
3759:                            TableResizer tableResizer =
3760:                            //                        new TableResizer(webform, tableBox.getDesignBean(), tableBox.tableDesignInfo,
3761:                            new TableResizer(webform, /*tableMarkupDesignBean, tableBox.tableDesignInfo,*/
3762:                                    tableComponentRootElement, CssBox.X_AXIS,
3763:                                    false, tableBox.getAbsoluteX(), top,
3764:                                    getHeight(), tableBox.getWidth(), minimum,
3765:                                    maximum, row, col, element);
3766:
3767:                            return tableResizer;
3768:                        }
3769:                    }
3770:
3771:                    return null;
3772:                }
3773:            }
3774:
3775:            static class OccupiedBox extends CellBox {
3776:                OccupiedBox() {
3777:                    super (null, null, BoxType.NONE, true, null);
3778:                }
3779:
3780:                public void initialize() {
3781:                }
3782:
3783:                protected void initializeInvariants() {
3784:                }
3785:
3786:                protected void initializeBackgroundDelayed() {
3787:                }
3788:
3789:                //        public String toString() {
3790:                //            return "OCCUPIED"; // NOI18N
3791:                //        }
3792:
3793:                public boolean isPlaceHolder() {
3794:                    return true;
3795:                }
3796:            }
3797:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.