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 <table> 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 <col> or a <td> or a <th>), 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: }
|