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 org.netbeans.modules.visualweb.api.designer.cssengine.CssListValue;
0044: import org.netbeans.modules.visualweb.api.designer.cssengine.CssProvider;
0045: import org.netbeans.modules.visualweb.api.designer.cssengine.CssValue;
0046: import org.netbeans.modules.visualweb.api.designer.markup.MarkupService;
0047: import org.netbeans.modules.visualweb.designer.CssUtilities;
0048: import java.awt.Color;
0049: import java.awt.Font;
0050: import java.awt.FontMetrics;
0051: import java.awt.Graphics;
0052: import java.awt.Point;
0053: import java.awt.Rectangle;
0054: import java.io.PrintStream;
0055: import java.io.PrintWriter;
0056: import java.util.ArrayList;
0057: import java.util.Arrays;
0058: import java.util.List;
0059:
0060: import javax.swing.JViewport;
0061:
0062: import org.openide.ErrorManager;
0063: import org.w3c.dom.Element;
0064: import org.w3c.dom.Node;
0065: import org.w3c.dom.NodeList;
0066:
0067: import org.netbeans.modules.visualweb.designer.DesignerUtils;
0068: import org.netbeans.modules.visualweb.designer.WebForm;
0069: import org.netbeans.modules.visualweb.api.designer.cssengine.XhtmlCss;
0070: import org.netbeans.modules.visualweb.designer.html.HtmlTag;
0071: import org.w3c.dom.Text;
0072:
0073: // For CVS archaeology: This file used to be called org.netbeans.modules.visualweb.css2.CssContainerBox
0074:
0075: /**
0076: * A CSS box which can hold children. Also a box which performs Css Layout!
0077: * @todo I'm handling inline parents (e.g. <span>) and block parents
0078: * (e.g. <div>, <p>, ...) differently. Perhaps instead of these if's in various
0079: * places, make separate CssInlineContainerBox and CssBlockContainer box classes
0080: * implementing the different semantics? (And a common parent for things like
0081: * defining the box list field.)
0082: *
0083: * @author Tor Norbye
0084: */
0085: public class ContainerBox extends CssBox {
0086: /** @todo Given that I support CSS2 whitespace handling now, shouldn't I nuke this flag? */
0087: static final boolean COLLAPSE = true;
0088: static Rectangle sharedClipRect = new Rectangle();
0089: BoxList boxes;
0090: protected boolean grid = false;
0091: protected boolean clipOverflow;
0092:
0093: public ContainerBox(WebForm webform, Element element,
0094: BoxType boxType, boolean inline, boolean replaced) {
0095: super (webform, element, boxType, inline, replaced);
0096: }
0097:
0098: protected void initialize() {
0099: super .initialize();
0100: initializeGrid();
0101:
0102: // Initialize overflow property
0103: Element element = getElement();
0104: if (element != null) {
0105: // Value val = CssLookup.getValue(element, XhtmlCss.OVERFLOW_INDEX);
0106: CssValue cssValue = CssProvider.getEngineService()
0107: .getComputedValueForElement(element,
0108: XhtmlCss.OVERFLOW_INDEX);
0109:
0110: // if (val != CssValueConstants.VISIBLE_VALUE) {
0111: if (!CssProvider.getValueService().isVisibleValue(cssValue)) {
0112: clipOverflow = true;
0113: }
0114: }
0115: }
0116:
0117: /** Return true iff the given box is "opaque", e.g. paints its own
0118: * contents, in which case we don't want to for example make it paint
0119: * its own background to erase grid dots underneath
0120: */
0121: protected boolean isOpaqueBox() {
0122: return false;
0123: }
0124:
0125: /** Return true iff this box is "on top of" a container with an
0126: * image -- unless a box in between this box and the image box
0127: * paints the background with a solid color */
0128: private boolean isAboveImage() {
0129: CssBox curr = getParent();
0130:
0131: while (curr != null) {
0132: if (curr.bgPainter != null) { // painter implies bg image
0133:
0134: return true;
0135: }
0136:
0137: if (curr.bg != null) {
0138: return false;
0139: }
0140:
0141: curr = curr.getParent();
0142: }
0143:
0144: return false;
0145: }
0146:
0147: protected void initializeBackground() {
0148: super .initializeBackground();
0149:
0150: ContainerBox parent = getParent();
0151: // Note: TableBox.CellBox doesn't call super.initializeBackground,
0152: // so if you do additional work here, check CellBox too.
0153: // XXX parent cannot be null here since background is an invariant and
0154: // is initialized from the constructor! Therefore, this code is
0155: // useless at the moment; I've gotta make it run later
0156: if (!isOpaqueBox()
0157: && (!inline || boxType.isAbsolutelyPositioned())
0158: && (bg == null) && (parent != null) && !grid
0159: && (parent.isGrid() || (parent.tag == HtmlTag.FORM))
0160: // special handling for the <form> tag - we want the grid to
0161: // shine through
0162: && (tag != HtmlTag.FORM) && !isAboveImage()) {
0163: // The parent has painted a grid, yet we're not a grid
0164: // component, so we've gotta "erase" the parent's grid dots
0165: CssBox p = parent;
0166:
0167: while ((p != null) && (p.bg == null)) {
0168: p = p.getParent();
0169: }
0170:
0171: if (p != null) {
0172: bg = p.bg;
0173: }
0174: }
0175: }
0176:
0177: /** Initialize whether this box should show a visual grid and should receive
0178: * grid mode handling from mouse operations */
0179: protected void initializeGrid() {
0180: if (!inline) {
0181: // Value val = CssLookup.getValue(getElement(), XhtmlCss.RAVELAYOUT_INDEX);
0182: CssValue cssValue = CssProvider.getEngineService()
0183: .getComputedValueForElement(getElement(),
0184: XhtmlCss.RAVELAYOUT_INDEX);
0185:
0186: // if (val == CssValueConstants.GRID_VALUE) {
0187: if (CssProvider.getValueService().isGridValue(cssValue)) {
0188: setGrid(true);
0189: }
0190: }
0191: }
0192:
0193: /** Set whether this is a grid-positioned box */
0194: public void setGrid(boolean grid) {
0195: this .grid = grid;
0196: }
0197:
0198: /** Indicate whether this is a grid-positioned box */
0199: public boolean isGrid() {
0200: if (tag == HtmlTag.FORM) {
0201: return grid || getParent().isGrid();
0202: }
0203:
0204: return grid;
0205: }
0206:
0207: /**
0208: * Return the list of boxes "managed" by this box. Managed simply
0209: * means that the coordinates in the boxes are all relative to this
0210: * one.
0211: */
0212: public int getBoxCount() {
0213: return (boxes != null) ? boxes.size() : 0;
0214: }
0215:
0216: /**
0217: * Return the box with the given index. There is no particular
0218: * significance to the index other than identifying a box; in particular
0219: * boxes with adjacent indices may not be adjacent visually.
0220: */
0221: public CssBox getBox(int index) {
0222: return boxes.get(index);
0223: }
0224:
0225: protected BoxList getBoxList() {
0226: return boxes;
0227: }
0228:
0229: public CssBox[] getBoxes() {
0230: if (boxes == null) {
0231: return new CssBox[0];
0232: }
0233:
0234: List<CssBox> boxList = new ArrayList<CssBox>();
0235: int count = boxes.size();
0236: for (int i = 0; i < count; i++) {
0237: CssBox box = boxes.get(i);
0238: boxList.add(box);
0239: }
0240:
0241: return boxList.toArray(new CssBox[boxList.size()]);
0242: }
0243:
0244: boolean containsChild(CssBox cssBox) {
0245: if (cssBox == null) {
0246: return false;
0247: }
0248: CssBox[] children = getBoxes();
0249: return Arrays.asList(children).contains(cssBox);
0250: }
0251:
0252: /**
0253: * Remove all the children boxes from the box list
0254: */
0255: protected void removeBoxes() {
0256: boxes = null;
0257: }
0258:
0259: /**
0260: * Add a new box to the list of boxes managed by this box
0261: */
0262: protected void addBox(CssBox box, CssBox prevBox, CssBox nextBox) {
0263: if (boxes == null) {
0264: //boxes = new ArrayList(8);
0265: int initialSize = 8;
0266:
0267: // XXX todo: pick an initial size based on the box we're
0268: // about to create; e.g. look at our node/element field,
0269: // look at the number of children, and do something based
0270: // on that. Typically we should do the number of element
0271: // nodes in the child (since most text nodes are just
0272: // whitespace formatting), but for LineBoxes we should do
0273: // something smarter.
0274: // Ditto for tables - we know roughly how many cells we're
0275: // going to add (non-rectangular tables or tables with
0276: // colspans and rowspans will be smaller).
0277: boxes = new BoxList(initialSize);
0278:
0279: if (boxType != BoxType.LINEBOX) {
0280: boxes.setKeepSorted(true);
0281: }
0282: }
0283:
0284: // This seems to be not needed yet.
0285: // // XXX #113899 Ensure the correct order. Due to complicated architecture,
0286: // // strange layout processing of LineBoxGroup sometimes leads to wrong order in the line box.
0287: // int size = boxes.size();
0288: // if (size > 0 && prevBox == null && nextBox == null) {
0289: // for (int i = 0; i < size; i++) {
0290: // CssBox sibling = boxes.get(i);
0291: // if(DesignerUtils.getNextSiblingElement(box.getElement()) == sibling.getElement()) {
0292: // nextBox = sibling;
0293: // break;
0294: // }
0295: // }
0296: // for (int i = size - 1; i >= 0; i--) {
0297: // CssBox sibling = boxes.get(i);
0298: // if (DesignerUtils.getPreviousSiblingElement(box.getElement()) == sibling.getElement()) {
0299: // prevBox = sibling;
0300: // break;
0301: // }
0302: // }
0303: // }
0304:
0305: boxes.add(box, prevBox, nextBox);
0306: box.setParent(this );
0307: box.setPositionedBy(this );
0308: }
0309:
0310: /**
0311: * Remove a particular box from the children list
0312: */
0313: protected boolean removeBox(CssBox box) {
0314: if (boxes == null) {
0315: // Internal error
0316: ErrorManager
0317: .getDefault()
0318: .log(
0319: "Unexpected box removal - box list is empty already");
0320:
0321: return false;
0322: }
0323:
0324: return boxes.remove(box);
0325: }
0326:
0327: /** XXX #112576.
0328: * @see LineBoxGroup#getBoxesToRemove */
0329: protected CssBox[] getBoxesToRemove(CssBox toRemove) {
0330: return new CssBox[] { toRemove };
0331: }
0332:
0333: /** This method can be called to make a hint as to how many
0334: * children will be added to this box. If the box already has
0335: * children, this method has no effect.
0336: */
0337: protected void setProbableChildCount(int children) {
0338: if (boxes == null) {
0339: boxes = new BoxList(children);
0340:
0341: if (boxType != BoxType.LINEBOX) {
0342: boxes.setKeepSorted(true);
0343: }
0344: }
0345: }
0346:
0347: /**
0348: * Return the last box in the format list for this box container, or
0349: * null if there are no boxes yet.
0350: * @todo Is this method unused now that I have getPrevNormalBox?
0351: */
0352:
0353: /*
0354: CssBox getLastBox() {
0355: return (getBoxCount() > 0) ? getBox(getBoxCount() - 1) : null;
0356: }
0357: */
0358:
0359: /** For debugging purposes only */
0360: static void printLayout(CssBox b, StringBuffer sb, int depth) {
0361: sb.append("\n");
0362:
0363: //Log.indent(depth);
0364: //sb.append("printLayout(box=" + box + ", depth=" + depth);
0365: for (int i = 0; i < depth; i++) {
0366: sb.append(" ");
0367: }
0368:
0369: sb.append(" [" + b.getX() + "," + b.getY() + ","
0370: + b.getWidth() + "," + b.getHeight() + "] ");
0371: sb.append(" BoxModel [");
0372:
0373: if ((b.boxType != null) && b.boxType.isPositioned()) {
0374: sb.append(b.left + "," + b.top + "," + b.right + ","
0375: + b.bottom + ",");
0376: }
0377:
0378: sb.append(b.contentWidth + "," + b.contentHeight + ", mrg="
0379: + b.leftMargin + "," + b.effectiveTopMargin + "] ");
0380: sb.append(" CB [" + b.containingBlockX + ","
0381: + b.containingBlockY + "," + b.containingBlockWidth
0382: + "," + b.containingBlockHeight + "] ");
0383:
0384: // sb.append(
0385: // " Color ["
0386: // + org.netbeans.modules.visualweb.designer.DesignerUtils.colorToStringName(b.bg)
0387: // + "] ");
0388: sb.append("\n");
0389:
0390: if (b.border != null) {
0391: sb.append(" Border [" + b.border.toString() + "]\n");
0392: }
0393:
0394: for (int i = 0; i < depth; i++) {
0395: sb.append(" ");
0396: }
0397:
0398: sb.append(b.toString());
0399:
0400: // if (lb == null) {
0401: for (int i = 0; i < depth; i++) {
0402: sb.append(" ");
0403: }
0404:
0405: sb.append(" [abs=" + b.getAbsoluteX() + "," + b.getAbsoluteY()
0406: + ", rel=" + b.getX() + "," + b.getY() + ", size="
0407: + b.getWidth() + "," + b.getHeight() + "] ");
0408:
0409: if (b.getBoxCount() > 0) {
0410: //Log.indent(depth);
0411: //sb.append("Num Boxes=" + box.getBoxCount());
0412: for (int l = 0; l < b.getBoxCount(); l++) {
0413: CssBox bc = b.getBox(l);
0414: sb.append("\n");
0415:
0416: for (int i = 0; i < (depth + 1); i++) {
0417: sb.append(" ");
0418: }
0419:
0420: sb.append("Box number " + l + "\n");
0421: printLayout(bc, sb, depth + 1);
0422: sb.append("\n");
0423: }
0424: }
0425: }
0426:
0427: /**
0428: * Recursively create and add boxes for all the children elements
0429: * of the element corresponding to this box. In other words, build
0430: * the box hierarchy. This will not do layout on the boxes - assigning
0431: * positions, sizes, or flowing inline content. org.netbeans.modules.visualweb.css2.FacesSupport.printNode(element)
0432: */
0433: protected void createChildren(CreateContext context) {
0434: Element element = getElement();
0435: if (element == null) {
0436: return;
0437: }
0438:
0439: NodeList list = element.getChildNodes();
0440: int len = list.getLength();
0441: setProbableChildCount(len);
0442:
0443: for (int i = 0; i < len; i++) {
0444: org.w3c.dom.Node child = (org.w3c.dom.Node) list.item(i);
0445:
0446: if ((child.getNodeType() == Node.TEXT_NODE)
0447: && COLLAPSE
0448: && DesignerUtils.onlyWhitespace(child
0449: .getNodeValue())) {
0450: continue;
0451: }
0452:
0453: addNode(context, child, null, null, null);
0454: }
0455: }
0456:
0457: /**
0458: * Add a box for the given child node
0459: */
0460: void addNode(CreateContext context, Node node,
0461: Element sourceElement, CssBox prevBox, CssBox nextBox) {
0462: // Find out what kind of box positioning we're dealing with
0463: if (node.getNodeType() == Node.ELEMENT_NODE) {
0464: Element element = (Element) node;
0465: String tagName = element.getTagName();
0466: HtmlTag tag = HtmlTag.getTag(tagName);
0467:
0468: if (tag == null) {
0469: if ((tagName.length() > 0)
0470: && Character.isUpperCase(tagName.charAt(0))) {
0471: // // TODO - line number?
0472: //// org.netbeans.modules.visualweb.insync.markup.MarkupUnit unit = webform.getMarkup();
0473: // Element e = MarkupService.getCorrespondingSourceElement(element);
0474: // String message =
0475: // NbBundle.getMessage(ContainerBox.class, "UppercaseTag", tagName);
0476: //// MarkupService.displayError(unit.getFileObject(), unit.computeLine(e), message);
0477: // Document doc = e.getOwnerDocument();
0478: // FileObject fo = InSyncService.getProvider().getFileObject(doc);
0479: // int line = InSyncService.getProvider().computeLine(doc, e);
0480: // InSyncService.getProvider().getRaveErrorHandler().displayErrorForFileObject(message, fo, line, 0);
0481: // XXX This validation should be done in parser, not here!
0482: ErrorManager.getDefault().notify(
0483: ErrorManager.INFORMATIONAL,
0484: new IllegalStateException(
0485: "The element has uppercase tag name, element="
0486: + element)); // NOI18N
0487: }
0488:
0489: // What do we do about unrecognized content? Should
0490: // probably process its children, right? E.g. if the
0491: // document contains <foobar>Hello World</foobar>,
0492: // "Hello World" should be shown even though it's
0493: // contained in an "unknown" element.
0494: NodeList nl = element.getChildNodes();
0495: int num = nl.getLength();
0496:
0497: if (num > 0) {
0498: setProbableChildCount(num); // or addProbablyChildCount?? XXX
0499:
0500: for (int i = 0, n = num; i < n; i++) {
0501: Node nn = nl.item(i);
0502:
0503: if ((nn.getNodeType() == Node.TEXT_NODE)
0504: && COLLAPSE
0505: && DesignerUtils.onlyWhitespace(nn
0506: .getNodeValue())) {
0507: continue;
0508: }
0509:
0510: addNode(context, nn, null, prevBox, nextBox); // Recurse
0511: }
0512: }
0513:
0514: return;
0515: } else {
0516: // XXX if element == body, it should be the case that
0517: // boxType == BoxType.STATIC -- we should ignore the position
0518: // and float properties! (See section 9.1.2)
0519: // Value display = CssLookup.getValue(element, XhtmlCss.DISPLAY_INDEX);
0520: CssValue cssDisplay = CssProvider.getEngineService()
0521: .getComputedValueForElement(element,
0522: XhtmlCss.DISPLAY_INDEX);
0523:
0524: // if (display == CssValueConstants.NONE_VALUE) {
0525: if (CssProvider.getValueService().isNoneValue(
0526: cssDisplay)) {
0527: return;
0528: }
0529:
0530: // boolean inline = isInlineTag(display, element, tag);
0531: boolean inline = isInlineTag(cssDisplay, element, tag);
0532:
0533: BoxType boxType = BoxType.getBoxType(element);
0534: boolean replaced = tag.isReplacedTag();
0535: CssBox box = null;
0536:
0537: // if (display == CssValueConstants.TABLE_VALUE /* || display == CssValueConstants.INLINE_TABLE_VALUE*/) {
0538: if (CssProvider.getValueService().isTableValue(
0539: cssDisplay)) {
0540: // TODO when the dust settles: move this logic into the box factory
0541:
0542: /*
0543: if (display == CssValueConstants.INLINE_TABLE_VALUE) {
0544: inline = true;
0545: replaced = true;
0546: }
0547: */
0548: box = TableBox.getTableBox(webform, element,
0549: boxType, inline, replaced);
0550: } else {
0551: box = BoxFactory.create(context, tag, webform,
0552: element, boxType, inline, replaced);
0553: }
0554:
0555: if (box == null) {
0556: // It's a hidden element - e.g. <input type="hidden">,
0557: // or perhaps some tag we've marked as visually hidden,
0558: // e.g. <area>.
0559: return;
0560: }
0561:
0562: if (sourceElement != null) {
0563: // This box was derived from a different source element
0564: // (e.g. a jsf tag which was rendered into an html tag)
0565: // so set up a mapping from the source element as well
0566: // CssBox.putBoxReference(sourceElement, box);
0567: putBoxReference(sourceElement, box);
0568: }
0569:
0570: addBoxNode(tag, box, context, prevBox, nextBox);
0571: }
0572: } else if ((node.getNodeType() == Node.TEXT_NODE)
0573: || (node.getNodeType() == Node.CDATA_SECTION_NODE)) {
0574: String content = node.getNodeValue();
0575: Element styleElement = null;
0576:
0577: if ((node.getParentNode() != null)
0578: && (node.getParentNode().getNodeType() == Node.ELEMENT_NODE)) {
0579: styleElement = (Element) node.getParentNode();
0580:
0581: // XXX I should just use context.element without above
0582: // check and cast - context.element should always
0583: // point to the nearest element from which I draw CSS
0584: // properties!
0585: //assert parent == this.element;
0586: //if (parent != this.element) {
0587: // System.out.println("Parent element was " + parent + " and this.element is " + this.element);
0588: //}
0589: }
0590:
0591: Text textNode = (node instanceof Text) ? (Text) node : null;
0592:
0593: // if (textNode != null) {
0594: // textNode = textNode.getSourceNode();
0595: // }
0596: addText(context, textNode, styleElement, content);
0597: }
0598: }
0599:
0600: /** Add the given child box to this parent box list, and
0601: * recursively create its children / box hiearchy. */
0602: protected void addBoxNode(HtmlTag tag, CssBox box,
0603: CreateContext context, CssBox prevBox, CssBox nextBox) {
0604: // Special handling when we're inserting in the middle of an
0605: // existing box tree
0606: if ((prevBox != null) || (nextBox != null)) {
0607: // For absolutely positioned boxes, we don't care about siblings
0608: if (!box.getBoxType().isAbsolutelyPositioned()) {
0609: addSiblingBoxNode(tag, box, context, prevBox, nextBox);
0610:
0611: return;
0612: }
0613:
0614: nextBox = null;
0615: prevBox = null;
0616: }
0617:
0618: // XXX just pass in null for the other puppies
0619: boolean finishLineBoxAfterChildren = false;
0620: boolean preserveLineBox = false;
0621: LineBoxGroup oldLineBox = null;
0622:
0623: if ((box.getBoxType() == BoxType.ABSOLUTE)
0624: || (box.getBoxType() == BoxType.FIXED)) {
0625: // Even inline tags should be positioned absolutely (not
0626: // added to a line box) but don't break the inline context
0627: finishLineBoxAfterChildren = true;
0628: preserveLineBox = true;
0629: oldLineBox = context.lineBox;
0630: context.lineBox = null;
0631: getBlockBox().addBox(box, prevBox, nextBox);
0632:
0633: if (box.getBoxType() == BoxType.FIXED) {
0634: context.addFixedBox(box);
0635: }
0636: } else if (box.getBoxType() == BoxType.FLOAT) {
0637: addToLineBox(context, box, null, null); // XXX Should finishLineBoxAfterChildren!
0638: preserveLineBox = true;
0639: oldLineBox = context.lineBox;
0640: context.lineBox = null;
0641: } else if (box.isBlockLevel()) {
0642: finishLineBox(context);
0643: getBlockBox().addBox(box, prevBox, nextBox); // redundant?
0644: finishLineBoxAfterChildren = true;
0645: } else {
0646: addToLineBox(context, box, null, null);
0647: }
0648:
0649: // // Create children of the box
0650: if (box instanceof ContainerBox) {
0651: ((ContainerBox) box).createChildren(context);
0652: }
0653:
0654: if (finishLineBoxAfterChildren) {
0655: finishLineBox(context);
0656: }
0657:
0658: if (preserveLineBox) {
0659: context.lineBox = oldLineBox;
0660: }
0661: }
0662:
0663: /** Add the given child box to this parent box list, and
0664: * recursively create its children / box hiearchy.
0665: * Deal with "prevBox" and "nextBox" to determine insertion
0666: * in the middle of the box hierarchy; used during insert events.
0667: *
0668: * @todo This code is really hacky; clean it up.
0669: */
0670: protected void addSiblingBoxNode(HtmlTag tag, CssBox box,
0671: CreateContext context, CssBox prevBox, CssBox nextBox) {
0672: boolean finishLineBoxAfterChildren = false;
0673: boolean preserveLineBox = false;
0674: LineBoxGroup oldLineBox = null;
0675:
0676: if ((box.getBoxType() == BoxType.ABSOLUTE)
0677: || (box.getBoxType() == BoxType.FIXED)) {
0678: // Absolute boxes do not care where they're inserted -
0679: // other than the actual painting/rendering order, since
0680: // z-order is sometimes related to the model/boxlist
0681: // position.
0682: // Even inline tags should be positioned absolutely (not
0683: // added to a line box) but don't break the inline context
0684: finishLineBoxAfterChildren = true;
0685: preserveLineBox = true;
0686: oldLineBox = context.lineBox;
0687: context.lineBox = null;
0688: getBlockBox().addBox(box, prevBox, nextBox);
0689:
0690: if (box.getBoxType() == BoxType.FIXED) {
0691: context.addFixedBox(box);
0692: }
0693: } else if (box.getBoxType() == BoxType.FLOAT) {
0694: // XXX how does this work?
0695: addToLineBox(context, box, prevBox, nextBox);
0696: preserveLineBox = true;
0697: oldLineBox = context.lineBox;
0698: context.lineBox = null;
0699: } else if (box.isBlockLevel()) {
0700: // Since we're not looking at absolute positioning, adjust prev/next
0701: // references to refer to normal children only
0702: while ((prevBox != null)
0703: && !(prevBox.getBoxType().isNormalFlow() || (prevBox
0704: .getBoxType().isInlineTextBox()))) {
0705: prevBox = prevBox.getPrevNormalBox();
0706: }
0707:
0708: while ((nextBox != null)
0709: && !(nextBox.getBoxType().isNormalFlow() || (nextBox
0710: .getBoxType().isInlineTextBox()))) {
0711: nextBox = nextBox.getNextNormalBox();
0712: }
0713:
0714: if ((prevBox != null) && (nextBox != null)
0715: && prevBox.isInlineBox() && nextBox.isInlineBox()) {
0716: // Block box is inserted in the middle of an inline context.
0717: // This means we need to split the linebox in half and
0718: // insert the block box in the middle.
0719: // XXX TODO
0720: LineBoxGroup lb = null;
0721:
0722: if (prevBox instanceof LineBoxGroup) {
0723: lb = (LineBoxGroup) prevBox; // happens when prev was absolutely positioned for example
0724: } else if (prevBox.getParent() instanceof LineBoxGroup) {
0725: lb = (LineBoxGroup) prevBox.getParent();
0726: } else {
0727: lb = (LineBoxGroup) prevBox.getParent().getParent();
0728: }
0729:
0730: // prev and next should be in the same lineboxgroup
0731: //assert lb == (nextBox.getParent() instanceof LineBoxGroup ?
0732: // (LineBoxGroup)nextBox.getParent() :
0733: // (LineBoxGroup)nextBox.getParent().getParent());
0734: // gotta check if nextBox instanceof LineBoxGroup too
0735: LineBoxGroup alb = lb.split(prevBox);
0736: context.prevChangedBox = lb;
0737: context.nextChangedBox = alb;
0738: getBlockBox().addBox(box, lb, null);
0739: getBlockBox().addBox(alb, box, null);
0740: } else {
0741: // We're either right above or right below a block box,
0742: // so simply insert the box where it needs to be.
0743: finishLineBox(context);
0744:
0745: if ((prevBox != null) && prevBox.isInlineBox()) {
0746: while ((prevBox != null)
0747: && !(prevBox instanceof LineBoxGroup)) {
0748: prevBox = prevBox.getParent();
0749: }
0750: }
0751:
0752: if ((nextBox != null) && nextBox.isInlineBox()) {
0753: while ((nextBox != null)
0754: && !(nextBox instanceof LineBoxGroup)) {
0755: nextBox = nextBox.getParent();
0756: }
0757: }
0758:
0759: getBlockBox().addBox(box, prevBox, nextBox);
0760: finishLineBoxAfterChildren = true;
0761: }
0762: } else {
0763: // Inline
0764: // Since we're not looking at absolute positioning, adjust prev/next
0765: // references to refer to normal children only
0766: while ((prevBox != null)
0767: && !(prevBox.getBoxType().isNormalFlow() || (prevBox
0768: .getBoxType().isInlineTextBox()))) {
0769: prevBox = prevBox.getPrevNormalBox();
0770: }
0771:
0772: while ((nextBox != null)
0773: && !(nextBox.getBoxType().isNormalFlow() || (nextBox
0774: .getBoxType().isInlineTextBox()))) {
0775: nextBox = nextBox.getNextNormalBox();
0776: }
0777:
0778: if ((prevBox != null) && (nextBox != null)
0779: && prevBox.isInlineBox() && nextBox.isInlineBox()) {
0780: // Inline box is inserted in the middle of an inline context.
0781: // This means we simply need to inject the inline box
0782: // into the middle of the LineBoxGroup
0783: LineBoxGroup lb = null;
0784:
0785: if (prevBox instanceof LineBoxGroup) {
0786: lb = (LineBoxGroup) prevBox; // happens when prev was absolutely positioned for example
0787: } else if (prevBox.getParent() instanceof LineBoxGroup) {
0788: lb = (LineBoxGroup) prevBox.getParent();
0789: } else {
0790: lb = (LineBoxGroup) prevBox.getParent().getParent();
0791: }
0792:
0793: // prev and next should be in the same lineboxgroup
0794: //assert lb == (nextBox.getParent() instanceof LineBoxGroup ?
0795: // (LineBoxGroup)nextBox.getParent() :
0796: // (LineBoxGroup)nextBox.getParent().getParent());
0797: // gotta check if nextBox instanceof LineBoxGroup too
0798: lb.addBox(box, prevBox, nextBox);
0799:
0800: // XXX #109446 Use just the created LineBoxGroup in the context.
0801: preserveLineBox = true;
0802: oldLineBox = context.lineBox;
0803: context.lineBox = lb;
0804: } else if ((prevBox != null) && prevBox.isInlineBox()) {
0805: // We're inserting an inline element between an
0806: // inline box and a block box; add it to the previous
0807: // linebox
0808: LineBoxGroup lb = null;
0809:
0810: if (prevBox instanceof LineBoxGroup) {
0811: lb = (LineBoxGroup) prevBox;
0812: prevBox = null;
0813: } else if (prevBox.getParent() instanceof LineBoxGroup) {
0814: lb = (LineBoxGroup) prevBox.getParent();
0815: } else {
0816: assert (prevBox.getParent() != null)
0817: && prevBox.getParent().getParent() instanceof LineBoxGroup;
0818: lb = (LineBoxGroup) prevBox.getParent().getParent();
0819: }
0820:
0821: lb.addBox(box, prevBox, null);
0822:
0823: // XXX #109446 Use just the created LineBoxGroup in the context.
0824: preserveLineBox = true;
0825: oldLineBox = context.lineBox;
0826: context.lineBox = lb;
0827: } else if ((nextBox != null) && nextBox.isInlineBox()) {
0828: // We're inserting an inline element between a
0829: // block box and an inline box; add it to the front
0830: // of the next linebox
0831: LineBoxGroup lb = null;
0832:
0833: if (nextBox instanceof LineBoxGroup) {
0834: lb = (LineBoxGroup) nextBox;
0835: nextBox = null;
0836: } else if (nextBox.getParent() instanceof LineBoxGroup) {
0837: lb = (LineBoxGroup) nextBox.getParent();
0838: } else {
0839: assert (nextBox.getParent() != null)
0840: && nextBox.getParent().getParent() instanceof LineBoxGroup;
0841: lb = (LineBoxGroup) nextBox.getParent().getParent();
0842: }
0843:
0844: lb.addBox(box, null, nextBox);
0845:
0846: // XXX #109446 Use just the created LineBoxGroup in the context.
0847: preserveLineBox = true;
0848: oldLineBox = context.lineBox;
0849: context.lineBox = lb;
0850: } else {
0851: // We're inserting an inline element between two block
0852: // boxes
0853: addToLineBox(context, box, prevBox, nextBox);
0854: }
0855: }
0856:
0857: // Create children of the box
0858: if (box instanceof ContainerBox) {
0859: ((ContainerBox) box).createChildren(context);
0860: }
0861:
0862: if (finishLineBoxAfterChildren) {
0863: finishLineBox(context);
0864: }
0865:
0866: if (preserveLineBox) {
0867: context.lineBox = oldLineBox;
0868: }
0869: }
0870:
0871: /**
0872: * For a given TEXT_NODE/CDATA_SECTION_NODE, add the text to the
0873: * current line box
0874: */
0875: protected void addText(CreateContext context, Text textNode,
0876: Element styleElement, String text) {
0877: // Check font size and attributes
0878: int decoration = 0;
0879: Color fg = Color.black;
0880: Color bg = null;
0881: FontMetrics metrics = context.metrics;
0882: boolean collapseSpaces = true;
0883: boolean hidden = false;
0884:
0885: if (styleElement != null) {
0886: // metrics = CssLookup.getFontMetrics(styleElement);
0887: // metrics = CssProvider.getValueService().getFontMetricsForElement(styleElement);
0888: metrics = CssUtilities.getDesignerFontMetricsForElement(
0889: styleElement, text, webform.getDefaultFontSize());
0890:
0891: // fg = CssLookup.getColor(styleElement, XhtmlCss.COLOR_INDEX);
0892: fg = CssProvider.getValueService().getColorForElement(
0893: styleElement, XhtmlCss.COLOR_INDEX);
0894:
0895: if (fg == null) {
0896: fg = Color.black;
0897: }
0898:
0899: //bg = Css.getColor(styleElement, XhtmlCssEngine.BACKGROUND_COLOR_INDEX);
0900: // Pick up the background color for text only when the background
0901: // color is set on an inline tag. If the background color
0902: // is coming from a block level box, then the background will
0903: // already have been painted as part of the block. Thus, this
0904: // will let us paint backgrounds when set on e.g. spans.
0905: // I also noticed Mozilla will inherit background colors when
0906: // set on inline tags - e.g.
0907: // <span style="background: red"><b><i>Has Red Bg</i></b></span>
0908: // So this code achieves that too; it's a bit of a hack since
0909: // it won't use proper selectors etc - when we redo the CSS
0910: // parser this should hopefully be taken care of by the
0911: // cascade!
0912: bg = null;
0913:
0914: CssBox curr = this ;
0915:
0916: while ((curr != null) && curr.inline && !curr.replaced) {
0917: if (curr.bg == null) {
0918: // XXX This is a hack! I should initialize this
0919: // earlier on.
0920: // XXX this should not be a problem anymore, I have initializeInvariants now
0921: curr.initializeBackground();
0922: }
0923:
0924: if (curr.bg != null) {
0925: bg = curr.bg;
0926:
0927: break;
0928: }
0929:
0930: curr = curr.getParent();
0931: }
0932:
0933: // Value val = CssLookup.getValue(styleElement, XhtmlCss.TEXT_DECORATION_INDEX);
0934: CssValue cssValue = CssProvider.getEngineService()
0935: .getComputedValueForElement(styleElement,
0936: XhtmlCss.TEXT_DECORATION_INDEX);
0937:
0938: // switch (val.getCssValueType()) {
0939: // case CSSValue.CSS_VALUE_LIST:
0940: // ListValue lst = CssLookup.getListValue(val);
0941: // if (lst == null) {
0942: // break;
0943: // }
0944: CssListValue cssListValue = CssProvider.getValueService()
0945: .getComputedCssListValue(cssValue);
0946: if (cssListValue != null) {
0947:
0948: // int len = lst.getLength();
0949: int len = cssListValue.getLength();
0950:
0951: for (int i = 0; i < len; i++) {
0952: // Value v = lst.item(i);
0953: CssValue cssV = cssListValue.item(i);
0954: // String s = v.getStringValue();
0955: String s = cssV.getStringValue();
0956:
0957: switch (s.charAt(0)) {
0958: case 'u':
0959: decoration |= TextBox.UNDERLINE;
0960:
0961: break;
0962:
0963: case 'o':
0964: decoration |= TextBox.OVERLINE;
0965:
0966: break;
0967:
0968: case 'l':
0969: decoration |= TextBox.STRIKE;
0970:
0971: break;
0972: }
0973: }
0974:
0975: // break;
0976: // default:
0977: } else {
0978: // XXX what happened?
0979: }
0980:
0981: // XXX Technically, should check for decoration=="overline" too...
0982: // (See section 16.3.1). However, does that have ANY practical
0983: // utility?
0984: // val = CssLookup.getValue(styleElement, XhtmlCss.WHITE_SPACE_INDEX);
0985: CssValue cssValue2 = CssProvider.getEngineService()
0986: .getComputedValueForElement(styleElement,
0987: XhtmlCss.WHITE_SPACE_INDEX);
0988:
0989: // if ((val == CssValueConstants.PRE_VALUE) || (val == CssValueConstants.PRE_WRAP_VALUE)) {
0990: if (CssProvider.getValueService().isPreValue(cssValue2)
0991: || CssProvider.getValueService().isPreWrapValue(
0992: cssValue2)) {
0993: collapseSpaces = false;
0994: }
0995:
0996: // val = CssLookup.getValue(getElement(), XhtmlCss.VISIBILITY_INDEX);
0997: CssValue cssValue3 = CssProvider.getEngineService()
0998: .getComputedValueForElement(getElement(),
0999: XhtmlCss.VISIBILITY_INDEX);
1000: // hidden = (val != CssValueConstants.VISIBLE_VALUE);
1001: hidden = !CssProvider.getValueService().isVisibleValue(
1002: cssValue3);
1003: } else {
1004: // Initialize metrics to something useful!
1005: ErrorManager.getDefault().log(
1006: "Gotta set font from somewhere else!");
1007: }
1008:
1009: addText(text, styleElement, textNode, context, metrics, fg, bg,
1010: decoration, collapseSpaces, hidden);
1011: }
1012:
1013: protected void addGrayItalicText(CreateContext context,
1014: Element styleElement, String text) {
1015: FontMetrics metrics = null;
1016:
1017: if (styleElement != null) {
1018: // metrics = CssLookup.getFontMetrics(styleElement);
1019: // metrics = CssProvider.getValueService().getFontMetricsForElement(styleElement);
1020: metrics = CssUtilities.getDesignerFontMetricsForElement(
1021: styleElement, text, webform.getDefaultFontSize());
1022:
1023: Font font = metrics.getFont();
1024: font = font.deriveFont(Font.ITALIC);
1025: // metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
1026: metrics = DesignerUtils.getFontMetrics(font);
1027: }
1028:
1029: if (metrics == null) {
1030: metrics = context.metrics;
1031: }
1032:
1033: addText(text, styleElement, null, context, metrics, Color.GRAY,
1034: null, 0, true, false);
1035: }
1036:
1037: /**
1038: * May add one or more boxes, each one of which may have box
1039: * children.
1040: * @todo Move this routine into static TextBox method?
1041: */
1042: private void addText(String content, Element styleElement,
1043: Text textNode, CreateContext context, FontMetrics metrics,
1044: Color fg, Color bg, int decoration, boolean collapseSpaces,
1045: boolean hidden) {
1046: // XXX separateTextBox(context);
1047: // Translate the string from jspx to xhtml
1048: // When doing visual position computations, we need to
1049: // reverse the computation.
1050: String source = content;
1051:
1052: // if ((textNode != null) && textNode.isJspx()) {
1053: if (textNode != null && MarkupService.isJspxNode(textNode)) {
1054: content =
1055: // <markup_separation>
1056: // MarkupServiceProvider.getDefault().expandHtmlEntities(content, true, styleElement);
1057: // ====
1058: // InSyncService.getProvider().expandHtmlEntities(content, true, styleElement);
1059: webform.getDomProviderService().expandHtmlEntities(content,
1060: true, styleElement);
1061: // </markup_separation>
1062: }
1063:
1064: if (styleElement != null) {
1065: // Value v1 = CssLookup.getValue(styleElement, XhtmlCss.FONT_VARIANT_INDEX);
1066: // Value v2 = CssLookup.getValue(styleElement, XhtmlCss.TEXT_TRANSFORM_INDEX);
1067: CssValue cssV1 = CssProvider.getEngineService()
1068: .getComputedValueForElement(styleElement,
1069: XhtmlCss.FONT_VARIANT_INDEX);
1070: CssValue cssV2 = CssProvider.getEngineService()
1071: .getComputedValueForElement(styleElement,
1072: XhtmlCss.TEXT_TRANSFORM_INDEX);
1073:
1074: // if ((v1 == CssValueConstants.SMALL_CAPS_VALUE) ||
1075: // (v2 == CssValueConstants.UPPERCASE_VALUE)) {
1076: if (CssProvider.getValueService().isSmallCapsValue(cssV1)
1077: || CssProvider.getValueService().isUpperCaseValue(
1078: cssV2)) {
1079: // Uppercase the text
1080: content = content.toUpperCase();
1081:
1082: // TODO (much later): split the text up like under capitalization
1083: // and apply different fonts to the initial letters
1084: // and the rest of the words. I can't trivially do that
1085: // here because I would create separate TextBoxes for the
1086: // initial character and the rest of the words, and this
1087: // COULD be split up both in text justification and in word
1088: // wrapping by the LineBox and LineBoxGroup containers, which
1089: // would be visually disasterous. I think the painting of
1090: // this would really have to be done in the TextBox itself.
1091: // } else if (v2 == CssValueConstants.LOWERCASE_VALUE) {
1092: } else if (CssProvider.getValueService().isLowerCaseValue(
1093: cssV2)) {
1094: content = content.toLowerCase();
1095: // } else if (v2 == CssValueConstants.CAPITALIZE_VALUE) {
1096: } else if (CssProvider.getValueService().isCapitalizeValue(
1097: cssV2)) {
1098: StringBuffer sb = new StringBuffer(content.length());
1099: boolean capitalize = false;
1100:
1101: for (int i = 0, n = content.length(); i < n; i++) {
1102: char c = content.charAt(i);
1103:
1104: if (Character.isWhitespace(c)) {
1105: capitalize = true;
1106: } else if (capitalize) {
1107: c = Character.toUpperCase(c);
1108: capitalize = false;
1109: }
1110:
1111: sb.append(c);
1112: }
1113:
1114: content = sb.toString();
1115: }
1116: }
1117:
1118: // Determine how much text will fit
1119: char[] contentChars = null;
1120: int len = content.length();
1121:
1122: /*
1123: From the HTML4.01 spec: make sure we handle this correctly:
1124:
1125: In Western scripts, for example, text should only be
1126: wrapped at white space. Early user agents incorrectly
1127: wrapped lines just after the start tag or just before
1128: the end tag of an element, which resulted in dangling
1129: punctuation. For example, consider this sentence:
1130:
1131: A statue of the <A href="cih78">Cihuateteus</A>, who are
1132: patron ...
1133:
1134: Wrapping the line just before the end tag of the A
1135: element causes the comma to be stranded at the beginning
1136: of the next line:
1137:
1138: A statue of the Cihuateteus
1139: , who are patron ...
1140:
1141: */
1142:
1143: // Find (and add) the next word
1144: int begin = 0;
1145: int end;
1146:
1147: if (!collapseSpaces) {
1148: end = len;
1149:
1150: if (contentChars == null) {
1151: contentChars = content.toCharArray(); // XXX share among views?
1152: }
1153:
1154: if (content.indexOf('\n') != -1) { // XXX what about \r and \r\n and \n\r?
1155:
1156: for (int i = begin; i < end; i++) {
1157: if (content.charAt(i) == '\n') {
1158: // Split here
1159: if (i > begin) {
1160: TextBox box = new TextBox(webform,
1161: styleElement, textNode,
1162: contentChars, content, source,
1163: begin, i, fg, bg, decoration,
1164: metrics, hidden);
1165: addToLineBox(context, box, null, null);
1166: }
1167:
1168: addToLineBox(context, new LineBreakBox(webform,
1169: styleElement, null), null, null);
1170: begin = i + 1;
1171: }
1172: }
1173:
1174: if (end > begin) {
1175: TextBox box = new TextBox(webform, styleElement,
1176: textNode, contentChars, content, source,
1177: begin, end, fg, bg, decoration, metrics,
1178: hidden);
1179: addToLineBox(context, box, null, null);
1180: }
1181:
1182: addToLineBox(context, new LineBreakBox(webform,
1183: styleElement, null), null, null);
1184: } else {
1185: TextBox box = new TextBox(webform, styleElement,
1186: textNode, contentChars, content, source, begin,
1187: end, fg, bg, decoration, metrics, hidden);
1188: addToLineBox(context, box, null, null);
1189: }
1190:
1191: return;
1192: }
1193:
1194: while (begin < len) {
1195: //char c = content.charAt(begin);
1196: if (Character.isWhitespace(content.charAt(begin))) {
1197: int spaceBegin = begin;
1198: begin++;
1199:
1200: // Skip remaining space characters
1201: while ((begin < len)
1202: && Character
1203: .isWhitespace(content.charAt(begin))) {
1204: begin++;
1205: }
1206:
1207: // XXX What if the linebox previous box is a space box?
1208: // if so there's nothing to do since the linebox
1209: // should never allow too consecutive space boxes...
1210: // However... I need to extend model range to include
1211: // my current range... Can this ever happen?
1212: SpaceBox box = new SpaceBox(webform, styleElement,
1213: textNode, content, source, spaceBegin, begin,
1214: fg, bg, decoration, metrics, hidden);
1215: addToLineBox(context, box, null, null);
1216: }
1217:
1218: // Find end of word
1219: end = begin;
1220:
1221: while ((end < len)
1222: && !Character.isWhitespace(content.charAt(end))) {
1223: end++;
1224: }
1225:
1226: if (end > begin) {
1227: if (contentChars == null) {
1228: contentChars = content.toCharArray(); // XXX share among views?
1229: }
1230:
1231: TextBox box = new TextBox(webform, styleElement,
1232: textNode, contentChars, content, source, begin,
1233: end, fg, bg, decoration, metrics, hidden);
1234: addToLineBox(context, box, null, null);
1235: begin = end;
1236: } else {
1237: break;
1238: }
1239: }
1240: }
1241:
1242: /**
1243: * Recompute the layout. Once the layout routine gets to a point
1244: * where the child layout matches the computed layout, it will leave
1245: * that tree alone. Thus, only the portions of the layout below
1246: * this box that need to be recomputed are updated
1247: *
1248: * @param cx X position of containing block
1249: * @param cy Y position of containing block
1250: * @param cw Width of containing block
1251: * @param ch Height of containing block
1252: */
1253: public void relayout(FormatContext context) {
1254: for (int i = 0, n = getBoxCount(); i < n; i++) {
1255: CssBox box = getBox(i);
1256:
1257: if (!box.isPlaceHolder()) {
1258: if ((box.getBoxType() == BoxType.LINEBOX) ||
1259:
1260: // Note: This is a bit inefficient: we will be positioning
1261: // the boxes more than once while there are floats in effect.
1262: // However, we need to ensure that boxes are at least roughly
1263: // positioned such that the "is floating box overlapping?" code
1264: // can compute relative distances between children of this box
1265: // and a common ancestor with a floating box still in effect.
1266: // And we can't simply move all positioning up to this point
1267: // because accurate block positioning requires the margin to
1268: // be computed, which isn't possible until vertical distances
1269: // have been computed (which has to be done after the children
1270: // have been laid out)
1271: (box.getBoxType().isNormalFlow() && (context.floats != null))) {
1272: positionBox(box, context);
1273: }
1274:
1275: layoutChild(box, context, true);
1276:
1277: // if ((box.getBoxType() != BoxType.LINEBOX) &&
1278: // (box.isBlockLevel() || box.getBoxType().isAbsolutelyPositioned())) {
1279: // XXX #113117 To be sure also the normal flow elements have position set.
1280: if ((box.getBoxType() != BoxType.LINEBOX)
1281: && (box.isBlockLevel()
1282: || box.getBoxType()
1283: .isAbsolutelyPositioned() || box
1284: .getBoxType().isNormalFlow())) {
1285: positionBox(box, context);
1286: }
1287: }
1288: }
1289: }
1290:
1291: /** @todo: make this a CssBox method instead - e.g. have relayout()
1292: * do this, and have relayout on children call super, then recurse! XXX
1293: * NO! This is passing values (such as the containing block) to the
1294: * child, that would have to be done by the parent first!
1295: */
1296: protected void layoutChild(CssBox box, FormatContext context,
1297: boolean handleChildren) {
1298: // Set new containing block size
1299: setContainingBlock(box, context);
1300:
1301: // Set up margin values etc.
1302: // XXX NO! This should be done at cration time, not layout time!
1303: // Oh wait - layout may write values into AUTO-fields.... hm....
1304: // add effectiveLeft/Right (perhaps call them "used" as in the spec)
1305: // and only mutate those from computeHorizontal/VerticalLengths
1306: box.initialize();
1307:
1308: box.computeHorizontalLengths(context);
1309:
1310: if (box.contentWidth != AUTO) {
1311: box.width = box.leftBorderWidth + box.leftPadding
1312: + box.contentWidth + box.rightPadding
1313: + box.rightBorderWidth;
1314: } else {
1315: box.width = UNINITIALIZED;
1316: }
1317:
1318: boolean abs = box.getBoxType().isAbsolutelyPositioned();
1319: List<FloatingBoxInfo> oldFloats = null;
1320: boolean oldFloating = false;
1321:
1322: if (abs) {
1323: // Absolute positioned children are not affected by floating boxes
1324: oldFloats = context.floats;
1325: oldFloating = context.floating;
1326: context.floats = null;
1327: context.floating = false;
1328: }
1329:
1330: // XXX #109564 This seems to caused the issue, but what it could cause now?
1331: // if(getBoxType() == BoxType.FLOAT) {
1332: // oldFloats = context.floats;
1333: // context.floats = null;
1334: // }
1335:
1336: if (handleChildren) {
1337: // Lay out the children now that we've established a containing
1338: // block and have constrained the content width if necessary.
1339: box.relayout(context);
1340: }
1341:
1342: // XXX #109564 This seems to caused the issue, but what it could cause now?
1343: // if(getBoxType() == BoxType.FLOAT) {
1344: // context.floats = oldFloats;
1345: // }
1346:
1347: if (abs) {
1348: context.floats = oldFloats;
1349: context.floating = oldFloating;
1350: }
1351:
1352: box.computeVerticalLengths(context);
1353: box.height = box.topBorderWidth + box.topPadding
1354: + box.contentHeight + box.bottomPadding
1355: + box.bottomBorderWidth;
1356:
1357: if (handleChildren && box instanceof ContainerBox) {
1358: //only now, when we calculated the height, we can move the relatives
1359: ((ContainerBox) box).finishAllRelatives(context);
1360: }
1361: }
1362:
1363: //to move the relatives, see if a child is a linebox
1364: void finishAllRelatives(FormatContext context) {
1365: for (int i = 0; i < getBoxCount(); i++) {
1366: CssBox box = getBox(i);
1367: if (box instanceof LineBoxGroup) {
1368: ((LineBoxGroup) box).finishRelatives(context);
1369: }
1370: }
1371: }
1372:
1373: /** Very similar to relayout(context) but does not compute
1374: * vertical dimensions, and does not position the boxes.
1375: * Used to initialize box dimensions for computation
1376: * of minimum widths when we're computing minimum widths
1377: * for table cells, etc.
1378: */
1379: protected void initializeHorizontalWidths(FormatContext context) {
1380: super .initializeHorizontalWidths(context);
1381:
1382: for (int i = 0, n = getBoxCount(); i < n; i++) {
1383: CssBox box = getBox(i);
1384:
1385: // We don't care about absolute/fixed children!
1386: if (box.getBoxType().isAbsolutelyPositioned()) {
1387: continue;
1388: }
1389:
1390: box.initializeHorizontalWidths(context);
1391: }
1392: }
1393:
1394: /** Set the containing block for the given box.
1395: * See section 10.1 of the CSS2.1 spec for details.
1396: */
1397: void setContainingBlock(CssBox box, FormatContext context) {
1398: // Containing Block Box - See CSS21 section 10.1 for logic.
1399: BoxType boxType = box.getBoxType();
1400:
1401: if ((boxType == BoxType.STATIC)
1402: || (boxType == BoxType.RELATIVE) // What about floats? They aren't mentioned in section 10.1, so I assume
1403: // they mean that floats do have a "static" position property
1404: || (boxType == BoxType.FLOAT)) {
1405: // if the element's position is 'relative' or 'static',
1406: // the containing block is formed by the content edge of
1407: // the nearest block-level, table cell or inline-block
1408: // ancestor box.
1409: CssBox cbb = box.getParent();
1410:
1411: while ((cbb != null)
1412: && !(cbb instanceof FrameBox)
1413: && !cbb.isBlockLevel()
1414: &&
1415: /*floats and relatives are part of normal flow, so*/
1416: !((boxType == BoxType.FLOAT || boxType == BoxType.RELATIVE) && cbb
1417: .getBoxType().isAbsolutelyPositioned())) {
1418: cbb = cbb.getParent();
1419: }
1420:
1421: if (cbb.contentWidth != AUTO) {
1422: box.containingBlockWidth = cbb.contentWidth;
1423: } else {
1424: box.containingBlockWidth = cbb.containingBlockWidth
1425: - cbb.leftMargin - cbb.leftBorderWidth
1426: - cbb.leftPadding - cbb.rightPadding
1427: - cbb.rightBorderWidth - cbb.rightMargin;
1428: }
1429:
1430: if (cbb.contentHeight != AUTO) {
1431: box.containingBlockHeight = cbb.contentHeight;
1432: } else {
1433: box.containingBlockHeight = cbb.containingBlockHeight
1434: - cbb.topMargin - cbb.topBorderWidth
1435: - cbb.topPadding - cbb.bottomPadding
1436: - cbb.bottomBorderWidth - cbb.bottomMargin;
1437: }
1438:
1439: // XXX how does x interact with box hierarchies (where children
1440: // are relative to their parents) and how about margins? effective
1441: // margins?
1442: box.containingBlockX = cbb.containingBlockX
1443: + cbb.leftMargin + cbb.leftBorderWidth
1444: + cbb.leftPadding;
1445: box.containingBlockY = cbb.containingBlockY + cbb.topMargin
1446: + cbb.topBorderWidth + cbb.topPadding;
1447: } else if (boxType == BoxType.LINEBOX) {
1448: // Line boxes simply keep their parent's containing blocks
1449:
1450: /* No - that doesn't work - for example, a <div width=70%>
1451: * may have a linebox - that line box should inherit the
1452: * content width - 70% - not the div's containing block
1453: * which is 100%...
1454: box.containingBlockX = containingBlockX;
1455: box.containingBlockY = containingBlockY;
1456: box.containingBlockWidth = containingBlockWidth;
1457: box.containingBlockHeight = containingBlockHeight;
1458: */
1459: /*
1460: box.containingBlockX = 0; //leftBorderWidth+leftPadding;
1461: box.containingBlockY = 0; //topBorderWidth+topPadding;
1462: // XXX is contentWidth initialized?
1463: box.containingBlockWidth = contentWidth-rightBorderWidth-rightPadding-leftBorderWidth-leftPadding;
1464: box.containingBlockHeight = contentHeight-bottomBorderWidth-bottomPadding-topBorderWidth-topPadding;
1465: */
1466: box.containingBlockX = leftMargin + leftBorderWidth
1467: + leftPadding;
1468: box.containingBlockY = topMargin + topBorderWidth
1469: + topPadding;
1470:
1471: // XXX is contentWidth initialized?
1472: box.containingBlockWidth = contentWidth;
1473: box.containingBlockHeight = contentHeight;
1474: } else if (boxType == BoxType.ABSOLUTE) {
1475: CssBox cbb = box.getParent();
1476:
1477: // Find nearest ancestor with a 'position' of 'absolute',
1478: // 'relative' or 'fixed'
1479: while ((cbb != null)
1480: && !(cbb.getBoxType().isPositioned() || (cbb.tag == HtmlTag.FRAME))) {
1481: cbb = cbb.getParent();
1482: }
1483:
1484: // We don't have a positioned parent: use the viewport
1485: if (cbb == null) {
1486: box.containingBlockX = 0;
1487: box.containingBlockY = 0;
1488: box.containingBlockWidth = context.initialWidth;
1489: box.containingBlockHeight = context.initialHeight;
1490: box.setPositionedBy(context.initialCB);
1491: } else if (cbb.isBlockLevel()) {
1492: /*
1493: // The containing block is formed by the padding edge of
1494: // the ancestor
1495: // XXX what if the ancestor has set a width? If so,
1496: // I should use it, shouldn't I?
1497: box.containingBlockWidth =
1498: cbb.containingBlockWidth - cbb.leftMargin - cbb.leftBorderWidth -
1499: cbb.rightBorderWidth - cbb.rightMargin;
1500:
1501: // XXX what about effective margins etc.?
1502: box.containingBlockHeight =
1503: cbb.containingBlockHeight - cbb.topMargin - cbb.topBorderWidth -
1504: cbb.bottomBorderWidth - cbb.bottomMargin;
1505:
1506: // XXX how does x interact with box hierarchies (where
1507: // children are relative to their parents) and how about
1508: // margins? effective margins?
1509: box.containingBlockX = cbb.containingBlockX + cbb.leftMargin + cbb.leftBorderWidth;
1510: box.containingBlockY = cbb.containingBlockY + cbb.topMargin + cbb.topBorderWidth;
1511: */
1512: box.containingBlockWidth = cbb.contentWidth
1513: + leftPadding + rightPadding;
1514: box.containingBlockHeight = cbb.contentHeight
1515: + topPadding + bottomPadding;
1516: box.containingBlockX = leftBorderWidth;
1517: box.containingBlockY = topBorderWidth;
1518: } else {
1519: // LTR assumption: the top and left of the containing
1520: // block are the top and left content edges of the first
1521: // box generated by the ancestor, and the bottom and
1522: // right are the bottom and right content edges of the
1523: // last box of the ancestor.
1524: // XXX revisit this code once I have redone the way
1525: // inline boxes are handled.
1526: // XXX I don't quite understand this rule. Or if I do,
1527: // it will be difficult to compute - we don't know the
1528: // last box of the ancestor yet - we haven't added it!
1529: // For now, just use the containing block assigned to
1530: // the ancestor.
1531: LineBoxGroup lbg = null;
1532:
1533: if (box.getParent() instanceof LineBoxGroup) {
1534: lbg = (LineBoxGroup) box.getParent();
1535: } else if ((box.getParent() != null)
1536: && box.getParent().getParent() instanceof LineBox) {
1537: lbg = (LineBoxGroup) box.getParent().getParent();
1538: } else {
1539: // XXX #6464191 TODO How should be this handled?
1540: // In this case box.getParent() was ContainerBox of the Group Panel.
1541: // assert false;
1542: }
1543:
1544: if (lbg != null) {
1545: box.containingBlockX = lbg.containingBlockX;
1546: box.containingBlockY = lbg.containingBlockY;
1547: box.containingBlockWidth = lbg.containingBlockWidth;
1548: box.containingBlockHeight = lbg.containingBlockHeight;
1549: }
1550: }
1551: } else if (boxType == BoxType.FIXED) {
1552: // If the element has 'position: fixed', the containing
1553: // block is established by the viewport.
1554: box.containingBlockX = 0;
1555: box.containingBlockY = 0;
1556: box.containingBlockWidth = context.initialWidth;
1557: box.containingBlockHeight = context.initialHeight;
1558: box.setPositionedBy(context.initialCB);
1559: } else {
1560: assert false : boxType;
1561: }
1562: }
1563:
1564: /** Position the box - set its x and y values relative to the
1565: * parent's border edge. Positioning depends on the box' BoxType.
1566: * This method should get called after margins etc. have been
1567: * computed properly (e.g. all AUTO values must be resolved).
1568: */
1569: protected void positionBox(CssBox box, FormatContext context) {
1570: // LineBoxes will do special handling of inline boxes; this
1571: // assert ensures that the LineBox overrides this method and
1572: // handles it correctly.
1573: //assert box.isBlockLevel();
1574: BoxType type = box.getBoxType();
1575:
1576: if ((type == BoxType.STATIC) || (type == BoxType.LINEBOX)) {
1577: positionBlockBox(box, context);
1578: } else if (type == BoxType.ABSOLUTE) {
1579: positionAbsoluteBox(box);
1580: } else if (type == BoxType.FLOAT) {
1581: positionFloatBox(box);
1582: } else if (type == BoxType.RELATIVE) {
1583: // XXX hm, shouldn't relative boxes be allowed in inline ctx
1584: // too? The LineBox needs to call this method as well!
1585: positionRelativeBox(box, context);
1586: } else if (type == BoxType.FIXED) {
1587: positionFixedBox(box, context);
1588: } else {
1589: assert false : type;
1590: }
1591: }
1592:
1593: private int calculateMargin(int prevMargin, int boxMargin) {
1594: // Handle negative offsets
1595: if ((prevMargin >= 0) && (boxMargin >= 0)) {
1596: // Normal case
1597: // The larger of adjacent margin values is used.
1598: return Math.max(prevMargin, boxMargin);
1599:
1600: // OLD:
1601: } else if ((prevMargin < 0) && (boxMargin < 0)) {
1602: // If the adjacent margins are all negative, the larger
1603: // of the negative values is used.
1604: // XXX this is not how I re-read the spec; it says "If
1605: // there are no positive margins, the absolute maximum
1606: // of the negative adjoining margins is deducted from
1607: // zero." So I take abs
1608: //margin = Math.max(-prevMargin, -boxMargin);
1609: return Math.min(prevMargin, boxMargin);
1610: } else {
1611: // If positive and negative vertical margins are
1612: // adjacent, the value should be collapsed thus: the
1613: // largest of the negative margin values should be
1614: // subtracted from the largest positive margin value.
1615: if ((prevMargin >= 0) && (boxMargin < 0)) {
1616: return prevMargin + boxMargin;
1617: } else {
1618: assert (prevMargin < 0) && (boxMargin >= 0);
1619: return boxMargin + prevMargin;
1620: }
1621: }
1622: }
1623:
1624: /** Position a box in block formatting context
1625: * @todo See computeEffectiveMargins and combine these
1626: */
1627: private void positionBlockBox(CssBox box, FormatContext context) {
1628: CssBox parentBox = box.getParent();
1629: assert parentBox != null;
1630:
1631: // 9.4.1: each box's left outer edge touches the left edge of the
1632: // containing block
1633: // This means that the distance between the child's border
1634: // edge and the parent's border edge, which we measure x relative to,
1635: // is parent padding plus child margin (since the parent padding
1636: // edge is aligned with the child outer edge. To get to the parent
1637: // border edge from the parent padding edge we add the parent padding,
1638: // and to get to the child border edge from the child outer edge,
1639: // we add the child margin.)
1640: box.setX(parentBox.leftPadding + parentBox.leftBorderWidth);
1641:
1642: // XXX Tables are block tags -- sorta -- but need different behavior
1643: // here. In particular, they should also be indented by floating
1644: // boxes:
1645: // compute y first, set to targetY
1646: //int leftEdge = context.getLeftEdge(box, targetY, box.getHeight());
1647: // TODO: look for "clear" property to clear floats - see section
1648: // 9.5.2 of the CSS2.1 spec
1649: // Collapsing vertical margins: specified in 8.3.1
1650: CssBox prevNormalBox = box.getPrevNormalBlockBox();
1651:
1652: if (prevNormalBox != null) {
1653: if (prevNormalBox instanceof LineBoxGroup) {
1654: prevNormalBox.computeVerticalLengths(context);
1655: }
1656: int newY = prevNormalBox.getY()
1657: + prevNormalBox.getHeight()
1658: + calculateMargin(prevNormalBox
1659: .getCollapsedBottomMargin(), box
1660: .getCollapsedTopMargin());
1661: box.setY(newY);
1662: box.effectiveTopMargin = 0;
1663: } else {
1664: // Else: this is the first normal block box we're adding;
1665: // this means we need to consider collapsing the nested
1666: // margins.
1667: // Margins should only collapse if there is no padding or
1668: // border separating them
1669: if ((parentBox.topBorderWidth == 0)
1670: && (parentBox.topPadding == 0)) {
1671: // Yes - collapse margins!
1672: // Collapse our parentBox.topMargin with box.topMargin -
1673: // except since this has a cumulative effect, gotta
1674: // collapse with the EFFECTIVE margin instead!
1675: // Handle negative offsets
1676: //int margin;
1677: int margin = parentBox.getCollapsedTopMargin();
1678:
1679: /*
1680: //int parentMargin = parentBox.topMargin;
1681: //int boxMargin = box.effectiveTopMargin;
1682: int parentMargin = parentBox.getCollapsedTopMargin();
1683: int boxMargin = box.getcollapsedMargin();
1684:
1685: if (parentMargin >= 0 && boxMargin >= 0) {
1686: // Normal case
1687: // The larger of adjacent margin values is used.
1688: margin =
1689: Math.max(parentMargin, boxMargin);
1690: // OLD:
1691: } else if (
1692: parentMargin < 0 && boxMargin < 0) {
1693:
1694: // If the adjacent margins are all negative, the larger
1695: // of the negative values is used.
1696:
1697: // XXX this is not how I re-read the spec; it says "If
1698: // there are no positive margins, the absolute maximum
1699: // of the negative adjoining margins is deducted from
1700: // zero." So I take abs
1701: //margin = Math.max(-parentMargin, -boxMargin);
1702: margin =
1703: Math.min(parentMargin, boxMargin);
1704: } else {
1705: // If positive and negative vertical margins are
1706: // adjacent, the value should be collapsed thus: the
1707: // largest of the negative margin values should be
1708: // subtracted from the largest positive margin value.
1709: if (parentMargin >= 0
1710: && boxMargin < 0) {
1711: margin = parentMargin + boxMargin;
1712: } else {
1713: assert parentMargin < 0
1714: && boxMargin >= 0;
1715: margin = boxMargin + parentMargin;
1716: }
1717: }
1718: */
1719: //6406309 fix
1720: //parentBox.effectiveTopMargin = margin;
1721: box.effectiveTopMargin = 0; // collapsed
1722:
1723: // All coordinates in this box will be relative to
1724: // the effectiveTopMargin
1725: // box.setY(0); // Since we've collapsed the box with our
1726: // own top margin, the box' border edge is located at
1727: // the same position as the paren'ts border edge.
1728: box.setY(parentBox.topBorderWidth);
1729: } else {
1730: // No, don't collapse margins
1731: box.setY(parentBox.topPadding
1732: + parentBox.topBorderWidth //);
1733: + box.effectiveTopMargin); // XXX Already added?
1734:
1735: parentBox.effectiveTopMargin = parentBox.topMargin;
1736: box.effectiveTopMargin = 0;
1737: }
1738: }
1739:
1740: //in case the box is clear box, we also need to look for a previous
1741: //float box
1742: if (box.isClearBox()/* && LineBoxGroup.findClearContainer(box) == null*/) {
1743: CssBox prevFloatBox = context.getPrevFloatingForClear(box);
1744: if (prevFloatBox != null) {
1745: int newY = context.adjustY(prevFloatBox.getHeight()
1746: + prevFloatBox.bottomMargin
1747: + prevFloatBox.topMargin, prevFloatBox, this )
1748: + box.topMargin;
1749: if (newY > box.getY()) {
1750: box.setY(newY);
1751: }
1752: }
1753:
1754: }
1755:
1756: /*
1757: if ((context.floats != null) && (box.getBoxType() != BoxType.LINEBOX) &&
1758: (box.getElement() != null)) {
1759: // Value clear = CssLookup.getValue(box.getElement(), XhtmlCss.CLEAR_INDEX);
1760: CssValue cssClear = CssProvider.getEngineService().getComputedValueForElement(box.getElement(), XhtmlCss.CLEAR_INDEX);
1761:
1762: // if (clear != CssValueConstants.NONE_VALUE) {
1763: if (!CssProvider.getValueService().isNoneValue(cssClear)) {
1764: // box.clearTop(context, clear);
1765: box.clearTop(context, cssClear);
1766: }
1767: }
1768: */
1769: }
1770:
1771: /** Position a box in block formatting context */
1772: private void positionRelativeBox(CssBox box, FormatContext context) {
1773: positionBlockBox(box, context);
1774: box.setLocation(box.getX() + box.left, box.getY() + box.top);
1775: }
1776:
1777: /** Position a box by absolute coordinates (where the coordinates
1778: * are relative to the containing block
1779: */
1780: private void positionAbsoluteBox(CssBox box) {
1781: box.setLocation(box.left + leftBorderWidth, box.top
1782: + topBorderWidth);
1783: }
1784:
1785: /** Position a box that is fixed to the viewport */
1786: private void positionFixedBox(CssBox box, FormatContext context) {
1787: ViewportBox vb = context.initialCB;
1788: JViewport viewport = vb.getViewport();
1789:
1790: if (viewport != null) {
1791: Point p = viewport.getViewPosition();
1792: box.setLocation(box.left + p.x, box.top + p.y);
1793: } else {
1794: box.setLocation(box.left, box.top);
1795: }
1796: }
1797:
1798: /** Position a box that is floated left or right
1799: * <p>
1800: * See section 9.5.1 for details.
1801: * </p>
1802: */
1803: private void positionFloatBox(CssBox box) {
1804: // Most of the work done in the LineBoxGroup!
1805: CssBox parentBox = box.getParent();
1806: assert parentBox != null;
1807:
1808: // Value floating = CssLookup.getValue(box.getElement(), XhtmlCss.FLOAT_INDEX);
1809: CssValue cssFloating = CssProvider.getEngineService()
1810: .getComputedValueForElement(box.getElement(),
1811: XhtmlCss.FLOAT_INDEX);
1812: boolean leftSide;
1813:
1814: // if (floating == CssValueConstants.LEFT_VALUE) {
1815: if (CssProvider.getValueService().isLeftValue(cssFloating)) {
1816: leftSide = true;
1817: // } else {
1818: // assert floating == CssValueConstants.RIGHT_VALUE;
1819: } else if (CssProvider.getValueService().isRightValue(
1820: cssFloating)) {
1821:
1822: // None not permitted since we wouldn't have identified a
1823: // float boxtype in the first place in BoxType.getBoxType
1824: leftSide = false;
1825: } else {
1826: ErrorManager.getDefault().notify(
1827: ErrorManager.INFORMATIONAL,
1828: new IllegalStateException(
1829: "Unexpected value, cssFloating="
1830: + cssFloating));
1831: return;
1832: }
1833:
1834: // 9.4.1: each box's left outer edge touches the left edge of the
1835: // containing block
1836: // This means that the distance between the child's border
1837: // edge and the parent's border edge, which we measure x relative to,
1838: // is parent padding plus child margin (since the parent padding
1839: // edge is aligned with the child outer edge. To get to the parent
1840: // border edge from the parent padding edge we add the parent padding,
1841: // and to get to the child border edge from the child outer edge,
1842: // we add the child margin.)
1843: if (leftSide) {
1844: box.setX(parentBox.leftPadding + parentBox.leftBorderWidth);
1845:
1846: // + box.leftMargin); // Already added?
1847: } else {
1848: // System.out.println("WRONG RIGHT POSITIONING!");
1849: // box.setX(parentBox.leftPadding + parentBox.leftBorderWidth);
1850: // XXX #99707 Based on the left float positioning above.
1851: box.setX(parentBox.width - parentBox.rightPadding
1852: - parentBox.rightBorderWidth);
1853: }
1854:
1855: // TODO: look for "clear" property to clear floats - see section
1856: // 9.5.2 of the CSS2.1 spec
1857: // Collapsing vertical margins: specified in 8.3.1
1858: CssBox prevBox = box.getPrevNormalBox();
1859:
1860: if (prevBox != null) {
1861: // We have a previous box - place the new box below it,
1862: // offset by the collapsed margins -
1863: // Handle negative offsets
1864: int margin;
1865:
1866: //int prevMargin = prevBox.effectiveBottomMargin;
1867: //int boxMargin = box.effectiveTopMargin;
1868: int prevMargin = prevBox.getCollapsedBottomMargin();
1869: int boxMargin = box.getCollapsedTopMargin();
1870:
1871: if ((prevMargin >= 0) && (boxMargin >= 0)) {
1872: // Normal case
1873: // The larger of adjacent margin values is used.
1874: margin = Math.max(prevMargin, boxMargin);
1875:
1876: // OLD:
1877: } else if ((prevMargin < 0) && (boxMargin < 0)) {
1878: // If the adjacent margins are all negative, the larger
1879: // of the negative values is used.
1880: // XXX this is not how I re-read the spec; it says "If
1881: // there are no positive margins, the absolute maximum
1882: // of the negative adjoining margins is deducted from
1883: // zero." So I take abs
1884: //margin = Math.max(-prevMargin, -boxMargin);
1885: margin = Math.min(prevMargin, boxMargin);
1886: } else {
1887: // If positive and negative vertical margins are
1888: // adjacent, the value should be collapsed thus: the
1889: // largest of the negative margin values should be
1890: // subtracted from the largest positive margin value.
1891: if ((prevMargin >= 0) && (boxMargin < 0)) {
1892: margin = prevMargin + boxMargin;
1893: } else {
1894: assert (prevMargin < 0) && (boxMargin >= 0);
1895: margin = boxMargin + prevMargin;
1896: }
1897: }
1898:
1899: box.setY( /*parentBox.topBorderWidth*/
1900: prevBox.getY() + prevBox.getHeight() + margin);
1901: box.effectiveTopMargin = 0;
1902: } else {
1903: // Else: this is the first normal block box we're adding;
1904: // this means we need to consider collapsing the nested
1905: // margins.
1906: // Margins should only collapse if there is no padding or
1907: // border separating them
1908: if ((parentBox.topBorderWidth == 0)
1909: && (parentBox.topPadding == 0)) {
1910: // Yes - collapse margins!
1911: // Collapse our parentBox.topMargin with box.topMargin -
1912: // except since this has a cumulative effect, gotta
1913: // collapse with the EFFECTIVE margin instead!
1914: // Handle negative offsets
1915: //int margin;
1916: int margin = parentBox.getCollapsedTopMargin();
1917:
1918: /*
1919: //int parentMargin = parentBox.topMargin;
1920: //int boxMargin = box.effectiveTopMargin;
1921: int parentMargin = parentBox.getCollapsedTopMargin();
1922: int boxMargin = box.getcollapsedMargin();
1923:
1924: if (parentMargin >= 0 && boxMargin >= 0) {
1925: // Normal case
1926: // The larger of adjacent margin values is used.
1927: margin =
1928: Math.max(parentMargin, boxMargin);
1929: // OLD:
1930: } else if (
1931: parentMargin < 0 && boxMargin < 0) {
1932:
1933: // If the adjacent margins are all negative, the larger
1934: // of the negative values is used.
1935:
1936: // XXX this is not how I re-read the spec; it says "If
1937: // there are no positive margins, the absolute maximum
1938: // of the negative adjoining margins is deducted from
1939: // zero." So I take abs
1940: //margin = Math.max(-parentMargin, -boxMargin);
1941: margin =
1942: Math.min(parentMargin, boxMargin);
1943: } else {
1944: // If positive and negative vertical margins are
1945: // adjacent, the value should be collapsed thus: the
1946: // largest of the negative margin values should be
1947: // subtracted from the largest positive margin value.
1948: if (parentMargin >= 0
1949: && boxMargin < 0) {
1950: margin = parentMargin + boxMargin;
1951: } else {
1952: assert parentMargin < 0
1953: && boxMargin >= 0;
1954: margin = boxMargin + parentMargin;
1955: }
1956: }
1957: */
1958: parentBox.effectiveTopMargin = margin;
1959: box.effectiveTopMargin = 0; // collapsed
1960:
1961: // All coordinates in this box will be relative to
1962: // the effectiveTopMargin
1963: // box.setY(0); // Since we've collapsed the box with our
1964: // own top margin, the box' border edge is located at
1965: // the same position as the paren'ts border edge.
1966: box.setY(parentBox.topBorderWidth);
1967: } else {
1968: // No, don't collapse margins
1969: box.setY(parentBox.topPadding
1970: + parentBox.topBorderWidth //);
1971: + box.effectiveTopMargin); // XXX Already added?
1972:
1973: parentBox.effectiveTopMargin = parentBox.topMargin;
1974: box.effectiveTopMargin = 0;
1975: }
1976: }
1977: }
1978:
1979: // /** Returns true if this box represents an inline span. This is true for
1980: // * inline boxes that are not block tags, or are not inline tags that are
1981: // * absolutely positioned, or are replaced.
1982: // */
1983: // private boolean isSpan() {
1984: // return inline && !boxType.isAbsolutelyPositioned() && !replaced;
1985: // }
1986: //
1987: // private static boolean isTextualBox(CssBox box) {
1988: // if (box == null) {
1989: // return false;
1990: // }
1991: // BoxType boxType = box.getBoxType();
1992: // return boxType == BoxType.TEXT || boxType == BoxType.SPACE || boxType == BoxType.LINEBREAK;
1993: // }
1994:
1995: /**
1996: * NOTE: prevBox and nextBox refer to the siblings for
1997: * the new LineBoxGroup to be created (if any), not where
1998: * within the LineBoxGroup to add the inline box!
1999: */
2000: private void addToLineBox(CreateContext context, CssBox ibox,
2001: CssBox prevBox, CssBox nextBox) {
2002: // Inline tags like <span> in flow context should simply include
2003: // the children as normal children; the LineBoxGroup will organize
2004: // them into LineBoxes
2005:
2006: // XXX #117840 This shuffle seems to be wrong, commented out.
2007: // // XXX #105679 This doesn't know how to deal with the 'textual' boxes, adding those normally.
2008: // if (isSpan() && !isTextualBox(ibox)) {
2009: // addBox(ibox, prevBox, nextBox);
2010: // } else {
2011: if (context.lineBox == null) {
2012: FontMetrics metrics = context.metrics;
2013:
2014: if (ibox.getBoxType() == BoxType.TEXT) {
2015: metrics = ((TextBox) ibox).getMetrics();
2016: } else if (ibox.getBoxType() == BoxType.SPACE) {
2017: metrics = ((SpaceBox) ibox).getMetrics();
2018: }
2019:
2020: context.lineBox = new LineBoxGroup(webform, getElement(),
2021: metrics);
2022:
2023: getBlockBox().addBox(context.lineBox, prevBox, nextBox);
2024: }
2025:
2026: context.lineBox.addBox(ibox, null, null);
2027: // }
2028: }
2029:
2030: /**
2031: * Finish the current line box we're working on; a new
2032: * addToLineBox call will generate a new line.
2033: */
2034: void finishLineBox(CreateContext context) {
2035: if (context.lineBox == null) {
2036: return;
2037: }
2038:
2039: if (context.lineBox.isEmpty()) {
2040: return;
2041: }
2042:
2043: context.lineBox = null;
2044: }
2045:
2046: public int getPrefMinWidth() {
2047: if (inline) {
2048: // Value val = CssLookup.getValue(getElement(), XhtmlCss.WHITE_SPACE_INDEX);
2049: CssValue cssValue = CssProvider.getEngineService()
2050: .getComputedValueForElement(getElement(),
2051: XhtmlCss.WHITE_SPACE_INDEX);
2052: // if ((val == CssValueConstants.PRE_VALUE) || (val == CssValueConstants.NOWRAP_VALUE)) {
2053: if (CssProvider.getValueService().isPreValue(cssValue)
2054: || CssProvider.getValueService().isNoWrapValue(
2055: cssValue)) {
2056: return getPrefWidth();
2057: }
2058: }
2059:
2060: int largest = 0;
2061: int n = getBoxCount();
2062:
2063: for (int i = 0; i < n; i++) {
2064: CssBox child = getBox(i);
2065:
2066: if (child.getBoxType().isAbsolutelyPositioned()) {
2067: continue;
2068: }
2069:
2070: int min = child.getPrefMinWidth();
2071:
2072: if (min > largest) {
2073: largest = min;
2074: }
2075: }
2076:
2077: if (leftMargin != AUTO) {
2078: largest += leftMargin;
2079: }
2080:
2081: if (rightMargin != AUTO) {
2082: largest += rightMargin;
2083: }
2084:
2085: // Borders and padding can't be left auto, can they?
2086: largest += (leftBorderWidth + leftPadding + rightBorderWidth + rightPadding);
2087:
2088: int curr = super .getPrefMinWidth();
2089:
2090: if (curr > largest) {
2091: largest = curr;
2092: }
2093:
2094: return largest;
2095: }
2096:
2097: public int getPrefWidth() {
2098: int largest = 0;
2099: int n = getBoxCount();
2100:
2101: if (inline && !boxType.isAbsolutelyPositioned()) {
2102: // Let the line box compute the size of these children
2103: CssBox curr = getParent();
2104:
2105: while ((curr != null) && !(curr instanceof LineBoxGroup)) {
2106: curr = curr.getParent();
2107: }
2108:
2109: if (curr != null) {
2110: largest = ((LineBoxGroup) curr).getPrefWidth(boxes);
2111: } else {
2112: // Shouldn't happen - this is just for safety right now
2113: // Inline tag: add up all the children and use that
2114: for (int i = 0; i < n; i++) {
2115: CssBox child = getBox(i);
2116:
2117: if (child.getBoxType().isAbsolutelyPositioned()) {
2118: continue;
2119: }
2120:
2121: // XXX does not properly handle LineBox.LINEBREAK
2122: largest += child.getPrefWidth();
2123: }
2124: }
2125: } else {
2126: // Block tag: find the widest child and use that
2127: for (int i = 0; i < n; i++) {
2128: CssBox child = getBox(i);
2129:
2130: if (child.getBoxType().isAbsolutelyPositioned()) {
2131: continue;
2132: }
2133:
2134: int min = child.getPrefWidth();
2135:
2136: if (min > largest) {
2137: largest = min;
2138: }
2139: }
2140: }
2141:
2142: if (leftMargin != AUTO) {
2143: largest += leftMargin;
2144: }
2145:
2146: if (rightMargin != AUTO) {
2147: largest += rightMargin;
2148: }
2149:
2150: // XXX #124104 There are some hacking their border sizes into contentHeight.
2151: // see FormComponentBox.
2152: if (!isBorderSizeIncluded()) {
2153: // Borders and padding can't be left auto, can they?
2154: largest += (leftBorderWidth + leftPadding
2155: + rightBorderWidth + rightPadding);
2156: }
2157:
2158: int curr = super .getPrefWidth();
2159:
2160: if (curr > largest) {
2161: largest = curr;
2162: }
2163:
2164: return largest;
2165: }
2166:
2167: protected boolean hasNormalBlockLevelChildren() {
2168: // XXX This gets tricky. Children boxes may be anonymous
2169: // block boxes - but I haven't been creating those!
2170: // Figure out how to handle this....
2171: int n = getBoxCount();
2172:
2173: for (int i = 0; i < n; i++) {
2174: CssBox child = getBox(i);
2175:
2176: if (child.getBoxType().isNormalFlow()) {
2177: if (!child.isInlineBox()) {
2178: return true;
2179: }
2180: }
2181: }
2182:
2183: return false;
2184: }
2185:
2186: /**
2187: * Notify the parent that the given child has been resized.
2188: * This might cause a reflow.
2189: * @return The topmost box in the hierarchy which had its size changed.
2190: */
2191: protected CssBox notifyChildResize(CssBox child,
2192: FormatContext context) {
2193: // Update positions to accomodate the new dimensions
2194: // of the given child
2195: // We don't check the parent pointer because pageBox should
2196: // override this method
2197: if (child.getParentIndex() != -1) {
2198: positionBox(child, context);
2199: }
2200:
2201: // If this is an absolutely positioned box, the parent has
2202: // no interest in our dimensions so we don't need to
2203: // propagate the size change.
2204: //if (boxType.isAbsolutelyPositioned()) {
2205: if (child.getBoxType().isAbsolutelyPositioned()) {
2206: return child;
2207: }
2208:
2209: // Move siblings further down in the flow
2210: if (child.getBoxType().isNormalFlow()) {
2211: // Adjust the remaining normal flow boxes too
2212: int i = child.getParentIndex() + 1;
2213: int n = getBoxCount();
2214:
2215: for (; i < n; i++) {
2216: CssBox sibling = getBox(i);
2217:
2218: if (sibling.getBoxType().isNormalFlow()) {
2219: positionBox(sibling, context);
2220: }
2221: }
2222: }
2223:
2224: ContainerBox parent = getParent();
2225: if (parent == null) {
2226: return this ; // page box: no furhter propagation is necessary
2227:
2228: // its contentHeight/height is computed from the extents,
2229: // which we will update manually
2230: }
2231:
2232: int oldWidth = contentWidth;
2233: int oldHeight = contentHeight;
2234: parent.layoutChild(this , context, false);
2235:
2236: // What about left/top, etc.?
2237: // Have the dimensions changed? If not, we're done
2238: if ((contentWidth == oldWidth) && (contentHeight == oldHeight)) {
2239: // TODO what if the effective margin has changed?
2240: // That might require us to propagate the change upwards
2241: // too!
2242:
2243: /*
2244: if (child.getParentIndex() != -1) {
2245: positionBox(child, context);
2246: }
2247: */
2248: return child;
2249: }
2250:
2251: // Otherwise, propagate the dimension change notification
2252: // to the parent so it can adjust its positions
2253: return parent.notifyChildResize(this , context);
2254: }
2255:
2256: protected void paintBox(Graphics g, int x, int y, int w, int h) {
2257: super .paintBox(g, x, y, w, h);
2258:
2259: // if (grid && GridHandler.getInstance().grid()) {
2260: // if (grid && getWebForm().getGridHandler().grid()) {
2261: // if (grid && GridHandler.getDefault().isGrid()) {
2262: if (grid && webform.isGridShow()) {
2263: if (hidden && !(this instanceof PageBox)) { // paint grid for the root pagebox!
2264:
2265: return;
2266: }
2267:
2268: paintGrid(g, x, y, w, h);
2269: }
2270: }
2271:
2272: protected void paintGrid(Graphics g, int x, int y, int w, int h) {
2273: //long start = System.currentTimeMillis();
2274: int width = w;
2275: int height = h;
2276:
2277: // On my U60, this takes roughly 24S-34 ms for the default
2278: // screensize
2279: Color oldColor = g.getColor();
2280: Color gridColor = webform.getColors().gridColor;
2281: assert gridColor != null;
2282: g.setColor(gridColor);
2283:
2284: // final int gridWidth = GridHandler.getInstance().getGridWidth();
2285: // final int gridHeight = GridHandler.getInstance().getGridHeight();
2286: // final int gridTraceWidth = GridHandler.getInstance().getGridTraceWidth();
2287: // final int gridTraceHeight = GridHandler.getInstance().getGridTraceHeight();
2288: // final int gridOffset = GridHandler.getInstance().getGridOffset();
2289: // GridHandler gridHandler = getWebForm().getGridHandler();
2290: // GridHandler gridHandler = GridHandler.getDefault();
2291: // final int gridWidth = gridHandler.getGridWidth();
2292: // final int gridHeight = gridHandler.getGridHeight();
2293: // final int gridTraceWidth = gridHandler.getGridTraceWidth();
2294: // final int gridTraceHeight = gridHandler.getGridTraceHeight();
2295: // final int gridOffset = gridHandler.getGridOffset();
2296: final int gridWidth = webform.getGridWidth();
2297: final int gridHeight = webform.getGridHeight();
2298: final int gridTraceWidth = webform.getGridTraceWidth();
2299: final int gridTraceHeight = webform.getGridTraceHeight();
2300: // final int gridOffset = webform.getGridOffset();
2301:
2302: // Draw a plain grid (one dot on each gridsize boundary
2303: //for (int x = gridOffset; x < width; x += gridSize) {
2304: // for (int y = gridOffset; y < height; y += gridSize) {
2305: // //g.drawLine(x, y, x, y);
2306: // // Is this faster?
2307: // g.fillRect(x, y, 1, 1);
2308: // }
2309: //}
2310: // int xstart = gridOffset;
2311: int xstart = 0;
2312:
2313: // Draw grid with smaller trace lines along the axes
2314: for (int xp = xstart; xp < width; xp += gridWidth) {
2315: for (int yp = gridTraceHeight; yp < height; yp += gridTraceHeight) {
2316: //g.drawLine(xp, yp, xp, yp);
2317: // Is this faster?
2318: g.fillRect(x + xp, y + yp, 1, 1);
2319: }
2320: }
2321:
2322: // int ystart = gridOffset;
2323: int ystart = 0;
2324:
2325: // This is going to redraw all the pixels on the gridsize*gridsize
2326: // grid - I can count gridTrace's and skip these. TODO.
2327: for (int xp = gridTraceWidth; xp < width; xp += gridTraceWidth) {
2328: for (int yp = ystart; yp < height; yp += gridHeight) {
2329: //g.drawLine(xp, yp, xp, yp);
2330: // Is this faster?
2331: g.fillRect(x + xp, y + yp, 1, 1);
2332: }
2333: }
2334:
2335: g.setColor(oldColor);
2336:
2337: //long end = System.currentTimeMillis();
2338: //System.out.println("Scan time for grid painting = " + (end-start) + " ms");
2339: }
2340:
2341: /** The iframe box should be clipped */
2342: public void paint(Graphics g, int px, int py) {
2343: if (clipOverflow) {
2344: g.getClipBounds(sharedClipRect);
2345:
2346: int cx = sharedClipRect.x;
2347: int cy = sharedClipRect.y;
2348: int cw = sharedClipRect.width;
2349: int ch = sharedClipRect.height;
2350: g.clipRect(px + leftMargin + getX(), py
2351: + effectiveTopMargin + getY(), width, height);
2352: super .paint(g, px, py);
2353: g.setClip(cx, cy, cw, ch);
2354: } else {
2355: super .paint(g, px, py);
2356: }
2357: }
2358:
2359: /** Prints a listing of this container box.
2360: * @see java.awt.Container#list(java.io.PrintStream, int) */
2361: public void list(PrintStream out, int indent) {
2362: super .list(out, indent);
2363:
2364: CssBox[] boxes = getBoxes();
2365: for (int i = 0; i < boxes.length; i++) {
2366: CssBox box = boxes[i];
2367: if (box != null) {
2368: box.list(out, indent + 1);
2369: }
2370: }
2371: }
2372:
2373: /** Prints a listing of this container box.
2374: * @see java.awt.Container#list(java.io.PrintWriter, int) */
2375: public void list(PrintWriter out, int indent) {
2376: super .list(out, indent);
2377:
2378: CssBox[] boxes = getBoxes();
2379: for (int i = 0; i < boxes.length; i++) {
2380: CssBox box = boxes[i];
2381: if (box != null) {
2382: box.list(out, indent + 1);
2383: }
2384: }
2385: }
2386:
2387: /** Finds all external form in this box tree. */
2388: public WebForm[] findExternalForms() {
2389: List<WebForm> externalForms = new ArrayList<WebForm>();
2390: CssBox[] boxes = getBoxes();
2391: for (CssBox box : boxes) {
2392: if (box instanceof ExternalDocumentBox) {
2393: ExternalDocumentBox externalDocBox = (ExternalDocumentBox) box;
2394: WebForm externalForm = externalDocBox.getExternalForm();
2395: if (externalForm != null) {
2396: externalForms.add(externalForm);
2397: }
2398: } else if (box instanceof ContainerBox) {
2399: ContainerBox containerBox = (ContainerBox) box;
2400: WebForm[] webForms = containerBox.findExternalForms();
2401: externalForms.addAll(Arrays.asList(webForms));
2402: }
2403: }
2404: return externalForms.toArray(new WebForm[externalForms.size()]);
2405: }
2406:
2407: /** Determine if the given element represents an inline tag
2408: * @todo Move to Utilities ?
2409: */
2410: private/*public*/static boolean isInlineTag(CssValue cssDisplay,
2411: Element element, HtmlTag tag) {
2412: //// if (display == CssValueConstants.NONE_VALUE) {
2413: // if (CssProvider.getValueService().isNoneValue(cssDisplay)) {
2414: // return false;
2415: // }
2416: //
2417: //// if ((display == CssValueConstants.BLOCK_VALUE) ||
2418: //// (display == CssValueConstants.LIST_ITEM_VALUE) ||
2419: //// (display == CssValueConstants.TABLE_VALUE) ||
2420: //// (
2421: //// /* These are not always block
2422: //// display == CssValueConstants.COMPACT_VALUE ||
2423: //// display == CssValueConstants.RUN_IN_VALUE ||
2424: //// */
2425: //// display == CssValueConstants.INLINE_BLOCK_VALUE)) {
2426: // if (CssProvider.getValueService().isBlockValue(cssDisplay)
2427: // || CssProvider.getValueService().isListItemValue(cssDisplay)
2428: // || CssProvider.getValueService().isTableValue(cssDisplay)
2429: // || CssProvider.getValueService().isInlineBlockValue(cssDisplay)){
2430: // return false;
2431: //// } else if (display == CssValueConstants.INLINE_VALUE) {
2432: // } else if (CssProvider.getValueService().isInlineValue(cssDisplay)) {
2433: // return true;
2434: //
2435: // // TODO: Handle rest of constants appropriately.
2436: // // The "inline" boolean flag is too simplistic; we should
2437: // // store the formatting type here and do the right type
2438: // // of layout
2439: //
2440: // /*
2441: // CssValueConstants.COMPACT_VALUE,
2442: // CssValueConstants.INLINE_TABLE_VALUE,
2443: // CssValueConstants.MARKER_VALUE,
2444: // CssValueConstants.RUN_IN_VALUE,
2445: // CssValueConstants.TABLE_VALUE,
2446: // CssValueConstants.TABLE_CAPTION_VALUE,
2447: // CssValueConstants.TABLE_CELL_VALUE,
2448: // CssValueConstants.TABLE_COLUMN_VALUE,
2449: // CssValueConstants.TABLE_COLUMN_GROUP_VALUE,
2450: // CssValueConstants.TABLE_FOOTER_GROUP_VALUE,
2451: // CssValueConstants.TABLE_HEADER_GROUP_VALUE,
2452: // CssValueConstants.TABLE_ROW_VALUE,
2453: // CssValueConstants.TABLE_ROW_GROUP_VALUE,
2454: // */
2455: // } else {
2456: // // Else - use tag default
2457: // if (tag == null) {
2458: // tag = HtmlTag.getTag(element.getTagName());
2459: // }
2460: //
2461: // if (tag != null) {
2462: // return tag.isInlineTag();
2463: // }
2464: // }
2465: //
2466: // return true;
2467: return CssProvider.getValueService().isInlineTag(cssDisplay,
2468: element, tag);
2469: }
2470:
2471: /** If this is a block-level box, return self, otherwise
2472: * return the nearest block-level ancestor box.
2473: * Note that absolutely positioned inline boxes are considered
2474: * block boxes, and so are replaced inline boxes (e.g. iframe, stringbox).
2475: */
2476: private ContainerBox getBlockBox() {
2477: // XXX what about floats?
2478: if (!inline || boxType.isAbsolutelyPositioned()) {
2479: return this ;
2480: // } if (isSpan() && !isTextualBox(this)) {
2481: // // XXX #117840 Improving the tree layout.
2482: // return this;
2483: } else {
2484: ContainerBox blockBox = this ;
2485:
2486: while (blockBox.inline
2487: && !blockBox.boxType.isAbsolutelyPositioned()
2488: && !blockBox.replaced
2489: || !(blockBox instanceof ContainerBox)) {
2490: ContainerBox parent = blockBox.getParent();
2491: if (parent == null) {
2492: break;
2493: }
2494: blockBox = parent;
2495: }
2496:
2497: return blockBox;
2498: }
2499: }
2500:
2501: }
|