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.awt.Color;
0044: import java.awt.FontMetrics;
0045: import java.awt.Graphics;
0046: import java.awt.Rectangle;
0047: import java.util.ArrayList;
0048: import java.util.List;
0049:
0050: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition;
0051: import org.netbeans.modules.visualweb.api.designer.cssengine.CssProvider;
0052: import org.netbeans.modules.visualweb.api.designer.cssengine.CssValue;
0053: import org.netbeans.modules.visualweb.designer.CssUtilities;
0054: import org.netbeans.modules.visualweb.designer.DesignerPane;
0055: import org.netbeans.modules.visualweb.designer.WebForm;
0056: import org.netbeans.modules.visualweb.api.designer.cssengine.XhtmlCss;
0057:
0058: import org.openide.ErrorManager;
0059:
0060: import org.w3c.dom.Element;
0061: import org.w3c.dom.Node;
0062:
0063: /**
0064: * This class represents a LineBoxGroup; a box representing
0065: * a single Inline context, which will be rendered as a set
0066: * of LineBoxes managed by this LineBoxGroup.
0067: * This class also maintains floating boxes, since these are
0068: * positioned along with line boxes (and in particular, if a float
0069: * appears in the middle of a stack of line boxes, we need to know
0070: * exactly where to put it - and only the LineBoxGroup can handle this.)
0071: * @todo Rename to LineBoxGroup
0072: *
0073: * @author Tor Norbye
0074: */
0075: public class LineBoxGroup extends ContainerBox {
0076: //private int maxWidth;
0077: //private int nextX;
0078: private final FontMetrics metrics;
0079: private LineBox lineBox = null;
0080: private int targetY;
0081: // private ArrayList floats = null;
0082: List<CssBox> floats = null;
0083: private BoxList allBoxes;
0084:
0085: public LineBoxGroup(WebForm webform, Element element,
0086: FontMetrics metrics) {
0087: // A LineBox isn't exactly an inline box, but it's NOT a block
0088: // level box. Perhaps I should reverse the logic and call
0089: // the constructor parameter "blocklevel" instead.
0090: super (webform, element, BoxType.LINEBOX, true, false);
0091:
0092: // XXX why am I passing element to super? I shouldn't do that
0093: this .metrics = metrics;
0094:
0095: effectiveTopMargin = 0; // Is this still necessary?
0096: effectiveBottomMargin = 0;
0097: }
0098:
0099: /** LineBoxes do not have margins, borders, grids, etc. like regular
0100: * boxes.
0101: */
0102: protected void initialize() {
0103: effectiveTopMargin = 0;
0104: effectiveBottomMargin = 0;
0105: }
0106:
0107: /** LineBoxes do not have margins, borders, grids, etc. like regular
0108: * boxes.
0109: */
0110: protected void initializeInvariants() {
0111: }
0112:
0113: // Gotta override this one too since it gets called separately from initialize()
0114: protected void initializeBackground() {
0115: }
0116:
0117: public FontMetrics getMetrics() {
0118: return metrics;
0119: }
0120:
0121: /**
0122: * Report whether the line box is empty (contains no inline boxes).
0123: *
0124: * @return true iff there are no inline boxes in this linebox.
0125: */
0126: public boolean isEmpty() {
0127: return (allBoxes == null) || (allBoxes.size() == 0);
0128: }
0129:
0130: /**
0131: * The actual "box list" (as seen by CssBox.getBoxCount())
0132: * is not updated until we perform layout, since we'll need to
0133: * create multiple intermediate line boxes to fit the contents
0134: * depending on the actual layout width and float box positioning.
0135: * TODO - consider changing this box into something else,
0136: * e.g. LineBoxGroup
0137: */
0138: protected void addBox(CssBox box, CssBox prevBox, CssBox nextBox) {
0139: assert box.isInlineBox() || (box.getBoxType() == BoxType.FLOAT);
0140:
0141: if (allBoxes == null) {
0142: allBoxes = new BoxList(10); // XXX good default? Do statistics.
0143: } else if ((box.getBoxType() == BoxType.SPACE)
0144: && (allBoxes != null)
0145: && (allBoxes.get(allBoxes.size() - 1).getBoxType() == BoxType.SPACE)) {
0146: // Suppress repeated spaces. I really should make this into
0147: // an assertion and make it easier for clients of this method
0148: // to determine if we've already put a space box in so they
0149: // can avoid creating the box itself.
0150: return;
0151: }
0152:
0153: // XXX #109446 Ensure new box of another component is not put
0154: // inbetween of two boxes belonging to other component.
0155: if (prevBox != null) {
0156: Element newComponentRootElement = ModelViewMapper
0157: .findClosestComponentRootElement(webform, box
0158: .getElement());
0159: Element prevComponentRootElement = ModelViewMapper
0160: .findClosestComponentRootElement(webform, prevBox
0161: .getElement());
0162: if (newComponentRootElement != prevComponentRootElement) {
0163: CssBox tmpNextBox = getNextCssBox(allBoxes, prevBox);
0164: while (tmpNextBox != null) {
0165: Element nextComponentRootElement = ModelViewMapper
0166: .findClosestComponentRootElement(webform,
0167: tmpNextBox.getElement());
0168: if (prevComponentRootElement == nextComponentRootElement) {
0169: prevBox = tmpNextBox;
0170: nextBox = null;
0171: tmpNextBox = getNextCssBox(allBoxes, tmpNextBox);
0172: } else {
0173: break;
0174: }
0175: }
0176: }
0177: }
0178: if (nextBox != null) {
0179: Element newComponentRootElement = ModelViewMapper
0180: .findClosestComponentRootElement(webform, box
0181: .getElement());
0182: Element nextComponentRootElement = ModelViewMapper
0183: .findClosestComponentRootElement(webform, nextBox
0184: .getElement());
0185: if (newComponentRootElement != nextComponentRootElement) {
0186: CssBox tmpPrevBox = getPreviousCssBox(allBoxes, nextBox);
0187: while (tmpPrevBox != null) {
0188: Element prevComponentRootElement = ModelViewMapper
0189: .findClosestComponentRootElement(webform,
0190: tmpPrevBox.getElement());
0191: if (prevComponentRootElement == nextComponentRootElement) {
0192: prevBox = null;
0193: nextBox = tmpPrevBox;
0194: tmpPrevBox = getPreviousCssBox(allBoxes,
0195: tmpPrevBox);
0196: } else {
0197: break;
0198: }
0199: }
0200: }
0201: }
0202:
0203: allBoxes.add(box, prevBox, nextBox);
0204:
0205: // XXX #98826 Very suspicoius code leading to the issue.
0206: // Wrong parentage, while the boxes in the 'allBoxes' BoxList are not the real chilren,
0207: // the real chilidren are kept in the 'boxes' BoxList.
0208: // However to comment out the nexst line doesn't work either, a messy impl.
0209: box.setParent(this );
0210: box.setPositionedBy(this );
0211: }
0212:
0213: private static CssBox getNextCssBox(BoxList boxList, CssBox cssBox) {
0214: if (boxList == null || cssBox == null) {
0215: return null;
0216: }
0217:
0218: int boxIndex = boxList.indexOf(cssBox);
0219: int nextBoxIndex = boxIndex < boxList.size() - 1 ? boxIndex + 1
0220: : -1;
0221: if (nextBoxIndex == -1) {
0222: return null;
0223: } else {
0224: return boxList.get(nextBoxIndex);
0225: }
0226: }
0227:
0228: private static CssBox getPreviousCssBox(BoxList boxList,
0229: CssBox cssBox) {
0230: if (boxList == null || cssBox == null) {
0231: return null;
0232: }
0233:
0234: int boxIndex = boxList.indexOf(cssBox);
0235: int previousBoxIndex = boxIndex > 0 ? boxIndex - 1 : -1;
0236: if (previousBoxIndex == -1) {
0237: return null;
0238: } else {
0239: return boxList.get(previousBoxIndex);
0240: }
0241: }
0242:
0243: /**
0244: * Remove a box from the "master" box list, not the actual
0245: * children list (which contains line boxes)
0246: */
0247: @Override
0248: protected boolean removeBox(CssBox box) {
0249: if (allBoxes == null) {
0250: ErrorManager.getDefault().log(
0251: "Unexpected removeBox " + box
0252: + ": lbg already empty");
0253:
0254: return false;
0255: } else {
0256: boolean ret = allBoxes.remove(box);
0257:
0258: // // XXX #109306 Remove also sibling boxes representing the same element.
0259: // // XXX Why they don't have common parent?
0260: // Element element = box.getElement();
0261: // if (element != null) {
0262: // int size = allBoxes.size();
0263: // List<CssBox> toRemove = new ArrayList<CssBox>();
0264: // for (int i = 0; i < size; i++) {
0265: // CssBox siblingBox = allBoxes.get(i);
0266: // if (siblingBox == null) {
0267: // continue;
0268: // }
0269: // if (element == siblingBox.getElement()) {
0270: // toRemove.add(siblingBox);
0271: // }
0272: // }
0273: // for (CssBox siblingBoxToRemove : toRemove) {
0274: // allBoxes.remove(siblingBoxToRemove);
0275: // }
0276: // }
0277:
0278: if (allBoxes.size() == 0) {
0279: ContainerBox parent = getParent();
0280: // Linebox now empty - remove it
0281: if (parent != null) {
0282: parent.removeBox(this );
0283: }
0284: }
0285:
0286: return ret;
0287: }
0288: }
0289:
0290: /** XXX #112576. */
0291: @Override
0292: protected CssBox[] getBoxesToRemove(CssBox toRemove) {
0293: // XXX #109306 Remove also sibling boxes representing the same element.
0294: // XXX Why they don't have common parent?
0295: Element element = toRemove.getElement();
0296: if (element != null) {
0297: int size = allBoxes.size();
0298: List<CssBox> boxesToRemove = new ArrayList<CssBox>();
0299: for (int i = 0; i < size; i++) {
0300: CssBox siblingBox = allBoxes.get(i);
0301: if (siblingBox == null) {
0302: continue;
0303: }
0304: // XXX #119975 Need to check parentage to be sure.
0305: if (isSameOrParentElementOf(element, siblingBox
0306: .getElement())) {
0307: boxesToRemove.add(siblingBox);
0308: }
0309: }
0310: return boxesToRemove.toArray(new CssBox[boxesToRemove
0311: .size()]);
0312: }
0313: return new CssBox[] { toRemove };
0314: }
0315:
0316: protected CssBox notifyChildResize(CssBox child,
0317: FormatContext context) {
0318: int oldHeight = contentHeight;
0319: relayout(context);
0320:
0321: ContainerBox parent = getParent();
0322: if (contentHeight != oldHeight) {
0323: return parent.notifyChildResize(this , context);
0324: } else {
0325: // The linebox may be new; ensure that it's positioned
0326: parent.positionBox(this , context);
0327: }
0328:
0329: return this ;
0330: }
0331:
0332: /**
0333: * Lays out ALL the children; should ONLY be called as part of updateLayout.
0334: * @todo Find a way to enforce that.
0335: */
0336: protected void layoutChild(CssBox box, FormatContext context,
0337: boolean handleChildren) {
0338: // The notifyChildResize call will do a relayout anyway
0339:
0340: /*
0341: if (handleChildren) {
0342: relayout(context);
0343: }
0344: */
0345: }
0346:
0347: /**
0348: * NOTE: prevBox and nextBox refer to the siblings for
0349: * the new LineBoxGroup to be created (if any), not where
0350: * within the LineBoxGroup to add the inline box!
0351: */
0352: private void addToLineBox(FormatContext context, CssBox ibox) {
0353: if (lineBox == null) {
0354: int maxWidth = context.getMaxWidth(this , targetY);
0355: int indent = 0;
0356:
0357: Element element = getElement();
0358: if ((super .getBoxCount() == 0) && (element != null)) {
0359: // First line box should look at the text-indent property
0360: // indent = CssLookup.getLength(element, XhtmlCss.TEXT_INDENT_INDEX);
0361: indent = CssUtilities.getCssLength(element,
0362: XhtmlCss.TEXT_INDENT_INDEX);
0363:
0364: if (indent == AUTO) {
0365: indent = 0;
0366: }
0367: }
0368:
0369: lineBox = new LineBox(webform, element, maxWidth, indent);
0370: lineBox.setParent(this );
0371: lineBox.setContainingBlock(containingBlockX,
0372: containingBlockY, containingBlockWidth,
0373: containingBlockHeight);
0374: }
0375:
0376: lineBox.addBox(ibox, null, null);
0377: }
0378:
0379: /** Perform layout on the inline box contents.
0380: * This typically means splitting it up into multiple
0381: * sub-lineboxes.
0382: */
0383: public void relayout(FormatContext context) {
0384: // TODO - look up width by subtracting widths of floats!
0385: // But that means we need to know the current "y" coordinate
0386: // we're targeted for.
0387: if (allBoxes == null) {
0388: return;
0389: }
0390:
0391: // TODO - instead of recreating the line box each time,
0392: // simply diff and see if flow changes - if it doesn't,
0393: // we're golden
0394: removeBoxes();
0395:
0396: targetY = 0;
0397: lineBox = null; // redundant?
0398:
0399: // Compute final size
0400: // Since the LineBoxGroup is an anonymous box, it doesn't have margins
0401: // and can't have AUTO etc. set on it, so we simply sum up the children
0402: // sizes
0403: contentWidth = 0;
0404: contentHeight = 0;
0405:
0406: relayoutChildren(context, allBoxes, getElement());
0407:
0408: finishLine(context);
0409:
0410: //finishing relatives has been moved to ContainerBox
0411: //now is time to shift relative boxes around
0412: //note, though, that all the browsers paint relative boxes
0413: //on top (in front) of other boxes, as if they had a greater z-index.
0414: //This is, kind of, against the spec, which says (9.9):
0415: //"Boxes with the same stack level in a stacking context
0416: //are stacked bottom-to-top according to document tree order."
0417: //if we were implementing the spec the same way as browsers, we would
0418: //finish relatives after all inlines in the document.
0419: //finishRelatives(context);
0420:
0421: width = contentWidth;
0422: height = contentHeight;
0423: }
0424:
0425: private void relayoutChildren(FormatContext context, BoxList list,
0426: Element element) {
0427: if (list == null) {
0428: return;
0429: }
0430:
0431: int n = list.size();
0432: boolean wrap = true;
0433:
0434: if (element != null) {
0435: // Value v = CssLookup.getValue(element, XhtmlCss.WHITE_SPACE_INDEX);
0436: CssValue cssV = CssProvider.getEngineService()
0437: .getComputedValueForElement(element,
0438: XhtmlCss.WHITE_SPACE_INDEX);
0439: // wrap = v == CssValueConstants.NORMAL_VALUE;
0440: wrap = CssProvider.getValueService().isNormalValue(cssV);
0441: }
0442:
0443: // XXX #113899 Keeps processed (fixing the ordering) children.
0444: List<CssBox> processedChildren = new ArrayList<CssBox>();
0445:
0446: for (int i = 0; i < n; i++) {
0447: CssBox box = list.get(i);
0448:
0449: if (processedChildren.contains(box)) {
0450: continue;
0451: }
0452:
0453: //relative boxes are similar to float boxes
0454: if (box.getBoxType() == BoxType.FLOAT
0455: || box.getBoxType() == BoxType.RELATIVE) {
0456: if (box.isInlineBox() && !box.isReplacedBox()
0457: && box instanceof ContainerBox
0458: && box.getBoxType() != BoxType.FLOAT) { // XXX #99707 Excluding floats from here.
0459: LineBox oldLineBox = lineBox;
0460: int oldTargetY = targetY;
0461: int oldContentWidth = contentWidth;
0462: int oldContentHeight = contentHeight;
0463: int indent = 0; // XXX Can you have indents here?
0464: lineBox = new LineBox(webform, box.getElement(),
0465: containingBlockWidth, indent);
0466: lineBox.isFloated = box.getBoxType() == BoxType.FLOAT;
0467: lineBox.setParent(this );
0468: lineBox.setContainingBlock(containingBlockX,
0469: containingBlockY, containingBlockWidth,
0470: containingBlockHeight);
0471: relayoutChildren(context, ((ContainerBox) box)
0472: .getBoxList(), box.getElement());
0473: finishLine(context);
0474: lineBox = oldLineBox;
0475: targetY = oldTargetY;
0476: contentWidth = oldContentWidth;
0477: contentHeight = oldContentHeight;
0478: } else {
0479: if (box.getBoxType() == BoxType.RELATIVE) {
0480: super .layoutChild(box, context, true);
0481: //Ah, no. The float are to be added into the linebox
0482: //super.addBox(box, null, null);
0483: addToLineBox(context, box);
0484: //super.positionBox(box, context);
0485: } else {
0486: assert box.getBoxType() == BoxType.FLOAT;
0487: if (floats == null) {
0488: floats = new ArrayList<CssBox>();
0489: }
0490:
0491: super .addBox(box, null, null);
0492: floats.add(box);
0493: }
0494: }
0495:
0496: // Floats at the beginning of the line should be processed right away so
0497: // they are positioned here, not after any other text on this line
0498: if ((lineBox == null) || lineBox.isEmpty()) {
0499: if (floats != null) { // XXX is this right if lineBox.isFloated?
0500: finishFloats(context);
0501: }
0502: }
0503:
0504: // Should we do any kind of content width update here?
0505: } else if (box.getBoxType() == BoxType.SPACE) {
0506: //if (lineBox != null) { // Ignore space at line beginning
0507: if (!wrap || (lineBox == null) || lineBox.isEmpty()
0508: || lineBox.canFit(context, box, targetY)) {
0509: addToLineBox(context, box);
0510: } else {
0511: finishLine(context);
0512: addToLineBox(context, box);
0513: }
0514:
0515: //}
0516: } else if (box.getBoxType() == BoxType.LINEBREAK) {
0517: // Break the line here
0518: addToLineBox(context, box);
0519: finishLine(context);
0520:
0521: if (context.floats != null) {
0522: // Value clear = CssLookup.getValue(box.getElement(), XhtmlCss.CLEAR_INDEX);
0523: CssValue cssClear = CssProvider.getEngineService()
0524: .getComputedValueForElement(
0525: box.getElement(),
0526: XhtmlCss.CLEAR_INDEX);
0527:
0528: // if (clear != CssValueConstants.NONE_VALUE) {
0529: if (!CssProvider.getValueService().isNoneValue(
0530: cssClear)) {
0531: int cleared = context.clear(cssClear, null);
0532:
0533: if (cleared > Integer.MIN_VALUE) {
0534: int clearance = cleared
0535: - (getAbsoluteY() + targetY);
0536:
0537: if (clearance > 0) {
0538: targetY += clearance;
0539: contentHeight += clearance;
0540: height += clearance;
0541: }
0542: }
0543: }
0544: }
0545: } else if (!box.isReplacedBox()
0546: && box instanceof ContainerBox
0547: // XXX #6494312 Hack!!! Faking support of inline table, letting it to fall back to else clause.
0548: // TODO Provide full support for inline tables.
0549: && !(box instanceof TableBox)) {
0550: // TODO - if the box has dimensions (even without
0551: // contents), we still need to reserve space for it!!!
0552: //need to assign original container to all sub-boxes,
0553: //otherwise it will be lost.
0554: BoxList boxList = ((ContainerBox) box).getBoxList();
0555:
0556: if (boxList == null && box instanceof LineBoxGroup) {
0557: boxList = ((LineBoxGroup) box).allBoxes;
0558: }
0559:
0560: if (boxList != null) {
0561: int boxListSize = boxList.size();
0562: for (int j = 0; j < boxListSize; j++) {
0563: boxList.get(j).originalInlineContainer = box;
0564: }
0565:
0566: // // XXX #113899 Bad architecture, the textual nodes are assigned to line box group
0567: // // which mihgt also be a parent of container which holds the other silings (non-textual) of the textual nodes.
0568: // // That causes incorrect ordering of the boxes comparing to the original element structure.
0569: // // Here trying to hack it, i.e. to put the thing into original order.
0570: // for (int j = 0; j < boxListSize; j++) {
0571: // CssBox boxListBox = boxList.get(j);
0572: // CssBox nextBoxListBox = j + 1 < boxListSize ? boxList.get(j + 1) : null;
0573: // if (boxListBox instanceof ContainerBox && boxListBox.getBoxCount() == 0) {
0574: // // Could be a placeholder
0575: // Element boxListBoxElement = boxListBox.getElement();
0576: // for (int k = i + 1; k < n; k++) {
0577: // CssBox nextSibling = list.get(k);
0578: // if (boxListBoxElement == nextSibling.getElement()) {
0579: // // XXX #109446 Don't add if it is already in.
0580: // if (boxList.indexOf(nextSibling) == -1) {
0581: // // XXX Place the next sibling to the place holder position.
0582: // boxList.add(nextSibling, boxListBox, nextBoxListBox);
0583: // }
0584: // processedChildren.add(nextSibling);
0585: // }
0586: // }
0587: // }
0588: // }
0589:
0590: relayoutChildren(context, boxList, box.getElement());
0591: }
0592: } else {
0593: boolean formatContent = true;
0594:
0595: if (box.getBoxType() == BoxType.TEXT) {
0596: formatContent = false;
0597: } else if (box.getBoxType() == BoxType.SPACE) {
0598: formatContent = false;
0599: } // XXX what about replaced boxes
0600:
0601: if (formatContent) {
0602: // We need to do this later if we support complicated boxes in
0603: // inline context --- for now just initialize
0604: //box.initialize();
0605: //box.relayout(context);
0606: super .layoutChild(box, context, true);
0607:
0608: //No: positionBox(box, context);
0609: // XXX #113117 To be sure also the position is set.
0610: positionBox(box, context);
0611: }
0612:
0613: if (!wrap || (lineBox == null) || lineBox.isEmpty()
0614: || lineBox.canFit(context, box, targetY)) {
0615: addToLineBox(context, box);
0616: } else {
0617: finishLine(context);
0618: addToLineBox(context, box);
0619: }
0620: }
0621: }
0622: }
0623:
0624: private void finishLine(FormatContext context) {
0625: if ((lineBox == null) || lineBox.isEmpty()) {
0626: if (floats != null) { // XXX is this right if lineBox.isFloated?
0627: finishFloats(context);
0628: }
0629:
0630: return;
0631: }
0632:
0633: int lineHeight = lineBox.applyVerticalAlignments();
0634:
0635: if (lineHeight == 0) {
0636: if (metrics != null) {
0637: lineHeight = metrics.getHeight();
0638: } else {
0639: ErrorManager
0640: .getDefault()
0641: .log(
0642: "Big hack in computing line height for linebox!");
0643: lineHeight = 14;
0644: }
0645: }
0646:
0647: lineBox.contentWidth = lineBox.getActualWidth();
0648: lineBox.contentHeight = lineHeight;
0649: lineBox.width = lineBox.contentWidth;
0650: lineBox.height = lineBox.contentHeight;
0651:
0652: // Do after setWidth
0653: // XXX #117399 To render the list within float correctly.
0654: // XXX #117400 If there is float parent, skip.
0655: // if (!lineBox.isFloated) {
0656: int leftEdge;
0657: if (lineBox.isFloated || hasFloatParent(lineBox)) {
0658: // XXX #117871 Fixing the alignments inside floats.
0659: leftEdge = 0;
0660: } else {
0661: leftEdge = context.getLeftEdge(lineBox, this , targetY,
0662: lineHeight); // Pass in getParent() instead?
0663: }
0664: lineBox
0665: .applyHorizontalAlignments(leftEdge, lineHeight,
0666: context);
0667:
0668: if (lineBox.contentWidth > contentWidth) {
0669: contentWidth = lineBox.contentWidth;
0670: }
0671:
0672: contentHeight += lineBox.contentHeight;
0673:
0674: if (lineBox.getX() == UNINITIALIZED) {
0675: lineBox.setX(0);
0676: }
0677:
0678: lineBox.setY(targetY);
0679: super .addBox(lineBox, null, null);
0680: targetY += lineHeight;
0681:
0682: if ((lineBox != null) && lineBox.isFloated) {
0683: if (floats == null) {
0684: floats = new ArrayList<CssBox>();
0685: }
0686:
0687: floats.add(lineBox);
0688: lineBox = null;
0689: } else if (floats != null) {
0690: lineBox = null;
0691: finishFloats(context);
0692: } else {
0693: lineBox = null;
0694: }
0695: }
0696:
0697: private static boolean hasFloatParent(CssBox cssBox) {
0698: if (cssBox == null) {
0699: return false;
0700: }
0701:
0702: CssBox parentBox = cssBox.getParent();
0703: while (parentBox != null) {
0704: if (parentBox.getBoxType() == BoxType.FLOAT) {
0705: return true;
0706: }
0707: parentBox = parentBox.getParent();
0708: }
0709: return false;
0710: }
0711:
0712: void finishRelatives(FormatContext context) {
0713: //walk though all relative boxes and shift them
0714: int n = allBoxes.size();
0715:
0716: for (int i = 0; i < n; i++) {
0717: CssBox child = allBoxes.get(i);
0718: if (child.getBoxType() == BoxType.RELATIVE) {
0719: //in many cases, height of the container is not known
0720: //by the time relitive box gets positioned
0721: //so, first define a containing block again
0722: super .setContainingBlock(child, context);
0723: //then, need to recalculate the properties
0724: Element childElement = child.getElement();
0725: CssProvider.getEngineService()
0726: .uncomputeValueForElement(childElement,
0727: XhtmlCss.LEFT_INDEX);
0728: CssProvider.getEngineService()
0729: .uncomputeValueForElement(childElement,
0730: XhtmlCss.RIGHT_INDEX);
0731: CssProvider.getEngineService()
0732: .uncomputeValueForElement(childElement,
0733: XhtmlCss.TOP_INDEX);
0734: CssProvider.getEngineService()
0735: .uncomputeValueForElement(childElement,
0736: XhtmlCss.BOTTOM_INDEX);
0737: child.left = CssUtilities.getCssLength(childElement,
0738: XhtmlCss.LEFT_INDEX);
0739: child.right = CssUtilities.getCssLength(childElement,
0740: XhtmlCss.RIGHT_INDEX);
0741: child.top = CssUtilities.getCssLength(childElement,
0742: XhtmlCss.TOP_INDEX);
0743: child.bottom = CssUtilities.getCssLength(childElement,
0744: XhtmlCss.BOTTOM_INDEX);
0745: if (child.left == AUTO) {
0746: if (child.right == AUTO) {
0747: child.left = 0;
0748: child.right = 0;
0749: } else {
0750: child.left = -child.right;
0751: }
0752: } else {
0753: if (child.right == AUTO) {
0754: child.right = -child.left;
0755: } else {
0756: //overconstrained
0757: child.right = -child.left;
0758: }
0759: }
0760: if (child.top == AUTO) {
0761: if (child.bottom == AUTO) {
0762: child.top = 0;
0763: child.bottom = 0;
0764: } else {
0765: child.top = -child.bottom;
0766: }
0767: } else {
0768: if (child.bottom == AUTO) {
0769: child.bottom = -child.top;
0770: } else {
0771: //overconstrained
0772: child.bottom = -child.top;
0773: }
0774: }
0775: int newX = child.getX() + child.left;
0776: int newY = child.getY() + child.top;
0777: child.setLocation(newX, newY);
0778: }
0779: }
0780: }
0781:
0782: private void finishFloats(FormatContext context) {
0783: if (floats != null) {
0784: for (int i = 0, n = floats.size(); i < n; i++) {
0785: CssBox box = floats.get(i);
0786:
0787: if (box.getBoxType() != BoxType.LINEBOX) {
0788: // XXX true here - what if we're doing this layout as part
0789: // of a child notify - in that case we shouldn't relayout the children!
0790: super .layoutChild(box, context, true);
0791:
0792: // XXX #113117 To be sure also the position is set.
0793: positionBox(box, context); // TEMP
0794: }
0795:
0796: int theY = targetY;
0797: //if the line box is not the first one, and there's space
0798: //for a float in the one above, shift the float up
0799: //if(linebox != null)
0800: if (box.isClearBox()/* && findClearContainer(box) == null*/) {
0801: //"clear" float box needs to be positioned below any floating box
0802: //on the left/right/both depending on the "clear" property value
0803: //so, let's find the floating box positioned previously
0804: //with the biggest getAbsoluteY() + getHeight() value.
0805: ///is there a container which is "clear" itself?
0806: //9.5.2: It may be that the element itself has floating descendants;
0807: //the 'clear' property has no effect on those
0808: //now let's adjust the targetY value
0809: CssBox prev = context.getPrevFloatingForClear(box);
0810: if (prev != null) {
0811: //this box is "clear" and there are floats above it.
0812: int newY = context.adjustY(prev.getHeight()
0813: + prev.bottomMargin + prev.topMargin,
0814: prev, this )
0815: + box.topMargin;
0816: theY = Math.max(targetY, newY);
0817: }
0818: } else {
0819: //9.5.1. #5
0820: //The outer top of a floating box may not be higher than the outer top
0821: //of any block or floated box generated by an element earlier in the source document.
0822: CssBox prev = context.getPrevFloatingForFloat(box);
0823: if (prev != null) {
0824: int newY = context.adjustY(0, prev, this );
0825: theY = Math.max(targetY, newY);
0826: }
0827: }
0828:
0829: //now lets check if the floats fits here
0830: //9.5.1 #7
0831: //A left-floating box that has another left-floating
0832: //box to its left may not have its right outer edge to the right
0833: //of its containing block's right edge. (Loosely: a left float
0834: //may not stick out at the right edge, unless it is already
0835: //as far to the left as possible.) An analogous rule holds
0836: //for right-floating elements.
0837: if (context.getMaxWidth(box, this , theY, box
0838: .getHeight()) < box.getWidth()) {
0839: //move it to the new line, then
0840: CssBox prev = context
0841: .getLowestFloatingForFloat(box);
0842: if (prev != null) {
0843: int newY = context.adjustY(prev.getHeight(),
0844: prev, this );
0845: theY = Math.max(targetY, newY);
0846: }
0847: }
0848:
0849: positionFloatBox(theY, box, context);
0850: // positionFloatBox(targetY, box, context);
0851: }
0852: }
0853:
0854: floats = null;
0855: }
0856:
0857: // static CssBox findClearContainer(CssBox box) {
0858: // CssBox parent = box;
0859: // while((parent = parent.getParent()) != null) {
0860: // CssValue cssClear = CssProvider.getEngineService().
0861: // getComputedValueForElement(parent.getElement(), XhtmlCss.CLEAR_INDEX);
0862: //
0863: // if(CssProvider.getValueService().isBothValue(cssClear) ||
0864: // CssProvider.getValueService().isLeftValue(cssClear) ||
0865: // CssProvider.getValueService().isRightValue(cssClear))
0866: // return(parent);
0867: // }
0868: // return(null);
0869: // }
0870:
0871: // XXX why isn't this reusing the ContainerBox positionFloatBox code?
0872: private void positionFloatBox(int py, CssBox box,
0873: FormatContext context) {
0874: box.effectiveTopMargin = 0; // XXX ?
0875: box.effectiveBottomMargin = 0;
0876:
0877: CssBox parentBox = box.getParent();
0878: assert parentBox != null;
0879: assert parentBox == this ;
0880:
0881: // Value floating = CssLookup.getValue(box.getElement(), XhtmlCss.FLOAT_INDEX);
0882: CssValue cssFloating = CssProvider.getEngineService()
0883: .getComputedValueForElement(box.getElement(),
0884: XhtmlCss.FLOAT_INDEX);
0885: boolean leftSide;
0886:
0887: // if (floating == CssValueConstants.LEFT_VALUE) {
0888: if (CssProvider.getValueService().isLeftValue(cssFloating)) {
0889: leftSide = true;
0890: // } else {
0891: // assert floating == CssValueConstants.RIGHT_VALUE;
0892: } else if (CssProvider.getValueService().isRightValue(
0893: cssFloating)) {
0894:
0895: // None not permitted since we wouldn't have identified a
0896: // float boxtype in the first place in BoxType.getBoxType
0897: leftSide = false;
0898: } else {
0899: ErrorManager.getDefault().notify(
0900: ErrorManager.INFORMATIONAL,
0901: new IllegalStateException(
0902: "Unexpected floating value, cssFloating="
0903: + cssFloating));
0904: return;
0905: }
0906:
0907: // Add/position floating box: its margins do NOT collapse!!
0908: box.effectiveTopMargin = box.topMargin;
0909: box.effectiveBottomMargin = box.bottomMargin;
0910:
0911: // Locate the floating box at the next linebox position
0912: int px;
0913:
0914: if (leftSide) {
0915: px = context.getLeftEdge(box, this , py, box.getHeight());
0916: } else {
0917: px = context.getRightEdge(box, this , py, box.getHeight())
0918: - box.getWidth();
0919: }
0920:
0921: box.setLocation(px, py);
0922:
0923: // Further layout operations need to know about this float box
0924: // so lineboxes can be shortened
0925: context.addFloat(px, py, box, leftSide);
0926:
0927: //context.floating = oldFloating;
0928: }
0929:
0930: /**
0931: * {@inheritDoc}
0932: *
0933: * @todo Update this to do logic parallel to {@link relayoutChildren}, e.g.
0934: * when child is a non-replaced ContainerBox process its BoxList using the
0935: * below logic rather than just call its getPrefMinWidth method which does
0936: * not have LineBoxGroup semantics. Similarly, handle floating boxes
0937: * specially.
0938: */
0939: public int getPrefMinWidth() {
0940: if (allBoxes == null) {
0941: return 0;
0942: }
0943:
0944: boolean wrap = true;
0945:
0946: Element element = getElement();
0947: if (element != null) {
0948: // Value v = CssLookup.getValue(element, XhtmlCss.WHITE_SPACE_INDEX);
0949: CssValue cssV = CssProvider.getEngineService()
0950: .getComputedValueForElement(element,
0951: XhtmlCss.WHITE_SPACE_INDEX);
0952: // wrap = v == CssValueConstants.NORMAL_VALUE;
0953: wrap = CssProvider.getValueService().isNormalValue(cssV);
0954: }
0955:
0956: if (wrap) {
0957: int largest = 0;
0958: int n = allBoxes.size();
0959:
0960: for (int i = 0; i < n; i++) {
0961: CssBox child = allBoxes.get(i);
0962: int min;
0963:
0964: if (child.getBoxType() == BoxType.LINEBREAK) {
0965: min = 0;
0966: } else {
0967: min = child.getPrefMinWidth();
0968: }
0969:
0970: if (min > largest) {
0971: largest = min;
0972: }
0973: }
0974:
0975: return largest;
0976: } else {
0977: BoxList list = allBoxes;
0978: int max = 0;
0979: int line = 0;
0980: int n = list.size();
0981:
0982: for (int i = 0; i < n; i++) {
0983: CssBox child = list.get(i);
0984:
0985: if (child.getBoxType() == BoxType.LINEBREAK) {
0986: // Break line - start from scratch
0987: line = 0;
0988: } else {
0989: line += child.getPrefMinWidth();
0990: }
0991:
0992: if (line > max) {
0993: max = line;
0994: }
0995: }
0996:
0997: return max;
0998: }
0999: }
1000:
1001: /**
1002: * {@inheritDoc}
1003: *
1004: * @todo Update this to do logic parallel to {@link relayoutChildren}, e.g.
1005: * when child is a non-replaced ContainerBox process its BoxList using the
1006: * below logic rather than just call its getPrefWidth method which does
1007: * not have LineBoxGroup semantics. Similarly, handle floating boxes
1008: * specially.
1009: */
1010: public int getPrefWidth() {
1011: return getPrefWidth(allBoxes);
1012: }
1013:
1014: protected int getPrefWidth(BoxList list) {
1015: if (list == null) {
1016: return 0;
1017: }
1018:
1019: //for the sake of those floats, that belong to this line box group
1020: //(not to a line box), and have width in %
1021: //contentWidth = getParent().contentWidth;
1022:
1023: int max = 0;
1024: int line = 0;
1025: int n = list.size();
1026:
1027: for (int i = 0; i < n; i++) {
1028: CssBox child = list.get(i);
1029:
1030: if (child.getBoxType() == BoxType.LINEBREAK) {
1031: // Break line - start from scratch
1032: line = 0;
1033: } else {
1034: line += child.getPrefWidth();
1035: }
1036:
1037: if (line > max) {
1038: max = line;
1039: }
1040: }
1041:
1042: return max;
1043: }
1044:
1045: protected void initializeHorizontalWidths(FormatContext context) {
1046: if (allBoxes == null) {
1047: return;
1048: }
1049:
1050: for (int i = 0, n = allBoxes.size(); i < n; i++) {
1051: CssBox box = allBoxes.get(i);
1052:
1053: // We don't care about absolute/fixed children!
1054: if (box.getBoxType().isAbsolutelyPositioned()) {
1055: // Can this happen in a linebox?
1056: continue;
1057: }
1058:
1059: box.initializeHorizontalWidths(context);
1060: }
1061: }
1062:
1063: void computeHorizontalLengths(FormatContext context) {
1064: // LineBoxes are anonymous, so they should have no "auto" settings
1065: // on them
1066: }
1067:
1068: void computeVerticalLengths(FormatContext context) {
1069: //actually, it's not the lineboxgroup that needs to include the floats -
1070: //it's linebox's container. The code moved into getSizeWithFloats(FormatContext)
1071: /*
1072: // if the box (the contained of LineBoxGroup) is a floating box itself
1073: //its size should include nested floats. All the browsers do so.
1074: CssValue cssFloating = CssProvider.getEngineService().getComputedValueForElement(this.getElement(), XhtmlCss.FLOAT_INDEX);
1075: if (CssProvider.getValueService().isLeftValue(cssFloating) ||
1076: CssProvider.getValueService().isRightValue(cssFloating)) {
1077: //here we need to look for all boxes whos "float" property is set
1078: boolean foundAFloatBox = false;
1079:
1080: int top = Integer.MAX_VALUE;
1081: int bottom = Integer.MIN_VALUE;
1082: int n = getBoxCount();
1083:
1084: for (int i = 0; i < n; i++) {
1085: CssBox child = getBox(i);
1086:
1087: cssFloating = CssProvider.getEngineService().getComputedValueForElement(child.getElement(), XhtmlCss.FLOAT_INDEX);
1088:
1089: if (CssProvider.getValueService().isLeftValue(cssFloating) ||
1090: CssProvider.getValueService().isRightValue(cssFloating)) {
1091:
1092: if (child.getY() < top) {
1093: top = child.getY();
1094: }
1095:
1096: if ((child.getY() + child.getHeight()) > bottom) {
1097: bottom = child.getY() + child.getHeight();
1098: }
1099:
1100: foundAFloatBox = true;
1101: }
1102: }
1103: if(foundAFloatBox) {
1104: if (top != Integer.MAX_VALUE) {
1105: contentHeight = bottom - top;
1106: }
1107: }
1108: }
1109: */
1110: }
1111:
1112: int getSizeWithFloats() {
1113: // if the box (the contained of LineBoxGroup) is a floating box itself
1114: //its size should include nested floats. All the browsers do so.
1115: CssValue cssFloating = CssProvider.getEngineService()
1116: .getComputedValueForElement(this .getElement(),
1117: XhtmlCss.FLOAT_INDEX);
1118: if (CssProvider.getValueService().isLeftValue(cssFloating)
1119: || CssProvider.getValueService().isRightValue(
1120: cssFloating)) {
1121: //here we need to look for all boxes whos "float" property is set
1122: boolean foundAFloatBox = false;
1123:
1124: int top = Integer.MAX_VALUE;
1125: int bottom = Integer.MIN_VALUE;
1126: int n = getBoxCount();
1127:
1128: for (int i = 0; i < n; i++) {
1129: CssBox child = getBox(i);
1130:
1131: cssFloating = CssProvider.getEngineService()
1132: .getComputedValueForElement(child.getElement(),
1133: XhtmlCss.FLOAT_INDEX);
1134:
1135: if (CssProvider.getValueService().isLeftValue(
1136: cssFloating)
1137: || CssProvider.getValueService().isRightValue(
1138: cssFloating)) {
1139: if (child.getY() < top) {
1140: top = child.getY();
1141: }
1142: if ((child.getY() + child.getHeight()) > bottom) {
1143: bottom = child.getY() + child.getHeight();
1144: }
1145: foundAFloatBox = true;
1146: }
1147: }
1148: if (foundAFloatBox) {
1149: if (bottom != Integer.MAX_VALUE) {
1150: return bottom - top;
1151: }
1152: }
1153: }
1154: return 0;
1155: }
1156:
1157: /** Split this linebox, creating a new linebox containing all
1158: * the elements following the child box "lastBox"; the new
1159: * line box will containg the remaining elements, and this
1160: * line box will be truncated to only contain the elements
1161: * up to lastBox.
1162: */
1163: LineBoxGroup split(
1164: org.netbeans.modules.visualweb.css2.CssBox lastBox) {
1165: // Find the box to be inserted
1166: assert allBoxes != null;
1167:
1168: int pos = 0;
1169: int n = allBoxes.size();
1170:
1171: for (; pos < n; pos++) {
1172: if (allBoxes.get(pos) == lastBox) {
1173: break;
1174: }
1175: }
1176:
1177: assert pos < n;
1178: pos++;
1179:
1180: LineBoxGroup split = new LineBoxGroup(webform, getElement(),
1181: metrics);
1182: int remainder = n - pos;
1183: split.allBoxes = new BoxList(remainder + 4); // XXX good default? Do statistics.
1184:
1185: for (int i = pos; i < n; i++) {
1186: split.allBoxes.add(allBoxes.get(i), null, null);
1187: }
1188:
1189: // Fix parent pointers
1190: for (int i = 0; i < remainder; i++) {
1191: split.allBoxes.get(i).setParent(split);
1192: split.allBoxes.get(i).setPositionedBy(split);
1193: }
1194:
1195: allBoxes.truncate(pos);
1196:
1197: return split;
1198: }
1199:
1200: // // XXX TODO JSF specific, replace with the latter method.
1201: // public void computeRectangles(DesignBean component, List list) {
1202: // // Look through my line boxes, and for each item, walk back up the parent chain
1203: // // looking for the given live bean. When found, the leaf is added to the bounds.
1204: // Rectangle bounds = null;
1205: //
1206: // for (int i = 0, n = getBoxCount(); i < n; i++) {
1207: // // LineBoxGroups contain mostly LineBoxes, but can have floats too
1208: // CssBox box = getBox(i);
1209: //
1210: // if (box instanceof LineBox) {
1211: // LineBox lb = (LineBox)box;
1212: //
1213: // for (int j = 0, m = lb.getBoxCount(); j < m; j++) {
1214: // CssBox leaf = lb.getBox(j);
1215: //
1216: // // Is it conceivable that a single line context will have
1217: // // multiple separate segments for different live beans? If so
1218: // // I guess I should only join rectangles for -contiguous-
1219: // // sections of boxes
1220: // if (hasComponentAncestor(leaf, component)) {
1221: // // Yessss
1222: // Rectangle r =
1223: // new Rectangle(leaf.getAbsoluteX(), leaf.getAbsoluteY(),
1224: // leaf.getWidth(), leaf.getHeight());
1225: //
1226: // if (bounds == null) {
1227: // // allocations get mutated by later
1228: // // transformations so I've gotta make a copy
1229: // bounds = r;
1230: // } else {
1231: // bounds.add(r);
1232: // }
1233: //
1234: // // We don't break here - multiple items in the line box group
1235: // // may be descendants and we want to include all in the bounds
1236: // // computation
1237: // }
1238: // }
1239: // } else {
1240: // assert box.getBoxType() == BoxType.FLOAT || box.getBoxType() == BoxType.RELATIVE;
1241: // box.computeRectangles(component, list);
1242: // }
1243: // }
1244: //
1245: // if (bounds != null) {
1246: // list.add(bounds);
1247: // }
1248: // }
1249:
1250: // XXX TODO This will replace the above.
1251: public void computeRectangles(Element componentRootElement,
1252: List<Rectangle> list) {
1253: // Look through my line boxes, and for each item, walk back up the parent chain
1254: // looking for the given live bean. When found, the leaf is added to the bounds.
1255: Rectangle bounds = null;
1256:
1257: for (int i = 0, n = getBoxCount(); i < n; i++) {
1258: // LineBoxGroups contain mostly LineBoxes, but can have floats too
1259: CssBox box = getBox(i);
1260:
1261: if (box instanceof LineBox) {
1262: LineBox lb = (LineBox) box;
1263:
1264: for (int j = 0, m = lb.getBoxCount(); j < m; j++) {
1265: CssBox leaf = lb.getBox(j);
1266:
1267: // Is it conceivable that a single line context will have
1268: // multiple separate segments for different live beans? If so
1269: // I guess I should only join rectangles for -contiguous-
1270: // sections of boxes
1271: Element element = leaf == null ? null : leaf
1272: .getElement();
1273: if (isSameOrParentElementOf(componentRootElement,
1274: element)
1275: || hasComponentAncestor(leaf,
1276: componentRootElement)) {
1277: // Yessss
1278: Rectangle r = new Rectangle(
1279: leaf.getAbsoluteX(), leaf
1280: .getAbsoluteY(), leaf
1281: .getWidth(), leaf.getHeight());
1282:
1283: if (bounds == null) {
1284: // allocations get mutated by later
1285: // transformations so I've gotta make a copy
1286: bounds = r;
1287: } else {
1288: bounds.add(r);
1289: }
1290:
1291: // We don't break here - multiple items in the line box group
1292: // may be descendants and we want to include all in the bounds
1293: // computation
1294: }
1295: }
1296: } else {
1297: assert box.getBoxType() == BoxType.FLOAT
1298: || box.getBoxType() == BoxType.RELATIVE;
1299: box.computeRectangles(componentRootElement, list);
1300: }
1301: }
1302:
1303: if (bounds != null) {
1304: list.add(bounds);
1305: }
1306: }
1307:
1308: // // XXX TODO JSF specific, replace with the latter method.
1309: // public Rectangle computeBounds(DesignBean component, Rectangle bounds) {
1310: // // Look through my line boxes, and for each item, walk back up the parent chain
1311: // // looking for the given live bean. When found, the leaf is added to the bounds.
1312: // for (int i = 0, n = getBoxCount(); i < n; i++) {
1313: // // LineBoxGroups contain mostly LineBoxes, but can have floats too
1314: // CssBox box = getBox(i);
1315: //
1316: // if (box instanceof LineBox) {
1317: // LineBox lb = (LineBox)box;
1318: //
1319: // for (int j = 0, m = lb.getBoxCount(); j < m; j++) {
1320: // CssBox leaf = lb.getBox(j);
1321: //
1322: // if (hasComponentAncestor(leaf, component)) {
1323: // // Yessss
1324: // Rectangle r =
1325: // new Rectangle(leaf.getAbsoluteX(), leaf.getAbsoluteY(),
1326: // leaf.getWidth(), leaf.getHeight());
1327: //
1328: // if (bounds == null) {
1329: // // allocations get mutated by later
1330: // // transformations so I've gotta make a copy
1331: // bounds = r;
1332: // } else {
1333: // bounds.add(r);
1334: // }
1335: //
1336: // // We don't break here - multiple items in the line box group
1337: // // may be descendants and we want to include all in the bounds
1338: // // computation
1339: // }
1340: // }
1341: // } else {
1342: // assert box.getBoxType() == BoxType.FLOAT || box.getBoxType() == BoxType.RELATIVE;
1343: // bounds = box.computeBounds(component, bounds);
1344: // }
1345: // }
1346: //
1347: // return bounds;
1348: // }
1349:
1350: // XXX TODO This will replace the above.
1351: public Rectangle computeBounds(Element componentRootElement,
1352: Rectangle bounds) {
1353: // Look through my line boxes, and for each item, walk back up the parent chain
1354: // looking for the given live bean. When found, the leaf is added to the bounds.
1355: for (int i = 0, n = getBoxCount(); i < n; i++) {
1356: // LineBoxGroups contain mostly LineBoxes, but can have floats too
1357: CssBox box = getBox(i);
1358:
1359: if (box instanceof LineBox) {
1360: LineBox lb = (LineBox) box;
1361:
1362: for (int j = 0, m = lb.getBoxCount(); j < m; j++) {
1363: CssBox leaf = lb.getBox(j);
1364:
1365: Element element = leaf == null ? null : leaf
1366: .getElement();
1367: if (isSameOrParentElementOf(componentRootElement,
1368: element)
1369: || hasComponentAncestor(leaf,
1370: componentRootElement)) {
1371: // Yessss
1372: Rectangle r = new Rectangle(
1373: leaf.getAbsoluteX(), leaf
1374: .getAbsoluteY(), leaf
1375: .getWidth(), leaf.getHeight());
1376:
1377: if (bounds == null) {
1378: // allocations get mutated by later
1379: // transformations so I've gotta make a copy
1380: bounds = r;
1381: } else {
1382: bounds.add(r);
1383: }
1384:
1385: // We don't break here - multiple items in the line box group
1386: // may be descendants and we want to include all in the bounds
1387: // computation
1388: }
1389: }
1390: } else {
1391: assert box.getBoxType() == BoxType.FLOAT
1392: || box.getBoxType() == BoxType.RELATIVE;
1393: bounds = box
1394: .computeBounds(componentRootElement, bounds);
1395: }
1396: }
1397:
1398: return bounds;
1399: }
1400:
1401: // // TODO JSF specific, this needs to be replaced by the latter method.
1402: // /** Return true iff the given leaf has a box as an ancestor (but below
1403: // * this LineBoxGroup) that corresponds to the given live bean */
1404: // private boolean hasComponentAncestor(CssBox leaf, DesignBean component) {
1405: // while ((leaf != null) && (leaf != this)) {
1406: //// if (leaf.getDesignBean() == component) {
1407: // if (getMarkupDesignBeanForCssBox(leaf) == component) {
1408: // return true;
1409: // }
1410: //
1411: // leaf = leaf.getParent();
1412: // }
1413: //
1414: // return false;
1415: // }
1416:
1417: // TODO This will replace the above method.
1418: /** Return true iff the given leaf has a box as an ancestor (but below
1419: * this LineBoxGroup) that corresponds to the given live bean */
1420: private boolean hasComponentAncestor(CssBox leaf,
1421: Element componentRootElement) {
1422: while ((leaf != null) && (leaf != this )) {
1423: // if (leaf.getDesignBean() == component) {
1424: // if (getElementForComponentRootCssBox(leaf) == componentRootElement) {
1425: // XXX #107084 There needs to be a way how to find a component for
1426: // the line box type of boxes, which otherwise don't have a component root element.
1427: // Before it was working only thanks to 'broken' hierarchy, the parent was different (ContainerBox)
1428: // than the actual box (LineBoxGroup) having one (TextBox) as its child.
1429: if (leaf.getElement() == componentRootElement) {
1430: return true;
1431: }
1432:
1433: leaf = leaf.getParent();
1434: }
1435:
1436: return false;
1437: }
1438:
1439: // XXX #118287 Also when the root box is not part of the tree (see also #107084).
1440: private static boolean isSameOrParentElementOf(
1441: Element parentElement, Element element) {
1442: if (parentElement == null || element == null) {
1443: return false;
1444: }
1445:
1446: Node node = element;
1447: while (node != null) {
1448: if (node == parentElement) {
1449: return true;
1450: }
1451: node = node.getParentNode();
1452: }
1453: return false;
1454: }
1455:
1456: /** FOR TESTSUITE ONLY! */
1457: public BoxList getManagedBoxes() {
1458: return allBoxes;
1459: }
1460:
1461: protected void paintBackground(Graphics g, int x, int y) {
1462: //LayeredHighlighter h = doc.getWebForm().getPane().getHighlighter();
1463: //h.paintLayeredHighlights(g, p0, p1, a, tc, this);
1464: DesignerPane pane = webform.getPane();
1465:
1466: if (pane == null) {
1467: return; // TESTSUITE
1468: }
1469:
1470: // DesignerCaret caret = pane.getCaret();
1471: // if ((caret != null) && caret.hasSelection()) {
1472: if (pane.hasCaretSelection()) {
1473: // Determine if the range intersects our line box group
1474: // Position sourceCaretBegin = caret.getFirstPosition();
1475: // DomPosition sourceCaretBegin = caret.getFirstPosition();
1476: DomPosition sourceCaretBegin = pane.getFirstPosition();
1477:
1478: // XXX I ought to have a cached method on the caret for obtaining the rendered
1479: // location!
1480: // Position caretBegin = sourceCaretBegin.getRenderedPosition();
1481: // Position sourceCaretEnd = caret.getLastPosition();
1482: // Position caretEnd = sourceCaretEnd.getRenderedPosition();
1483: DomPosition caretBegin = sourceCaretBegin
1484: .getRenderedPosition();
1485:
1486: // DomPosition sourceCaretEnd = caret.getLastPosition();
1487: DomPosition sourceCaretEnd = pane.getLastPosition();
1488:
1489: DomPosition caretEnd = sourceCaretEnd.getRenderedPosition();
1490:
1491: Node firstNode = findFirstNode();
1492:
1493: if (firstNode == null) {
1494: return;
1495: }
1496:
1497: Node lastNode = findLastNode();
1498: Node caretBeginNode = caretBegin.getNode();
1499:
1500: if (caretBeginNode == null) {
1501: return;
1502: }
1503:
1504: Node caretEndNode = caretEnd.getNode();
1505:
1506: if (caretEndNode == null) {
1507: return;
1508: }
1509:
1510: // int r1 =
1511: // Position.compareBoundaryPoints(caretBeginNode, caretBegin.getOffset(), lastNode,
1512: // 10000);
1513: int r1 = webform.compareBoundaryPoints(caretBeginNode,
1514: caretBegin.getOffset(), lastNode, 10000);
1515:
1516: // int r2 =
1517: // Position.compareBoundaryPoints(caretEndNode, caretEnd.getOffset(), firstNode, 0);
1518: int r2 = webform.compareBoundaryPoints(caretEndNode,
1519: caretEnd.getOffset(), firstNode, 0);
1520:
1521: if ((r1 >= 0) && (r2 <= 0)) {
1522: PageBox pageBox = pane.getPageBox();
1523:
1524: // TODO I should make sure modelToView uses the render nodes!
1525: // Rectangle p0 = pageBox.modelToView(sourceCaretBegin);
1526: // Rectangle p1 = pageBox.modelToView(sourceCaretEnd);
1527: Rectangle p0 = ModelViewMapper.modelToView(pageBox,
1528: sourceCaretBegin);
1529: Rectangle p1 = ModelViewMapper.modelToView(pageBox,
1530: sourceCaretEnd);
1531:
1532: if ((p0 != null) && (p1 != null)) {
1533: if ((p1.y < p0.y)
1534: || ((p0.y == p1.y) && (p1.x < p0.x))) {
1535: // Swap to make sure mark comes visually before dot
1536: Rectangle temp = p0;
1537: p0 = p1;
1538: p1 = temp;
1539: }
1540:
1541: g.setColor(pane.getSelectionColor());
1542:
1543: if (p0.y == p1.y) {
1544: // same line, render a rectangle
1545: // No - shouldn't include the width on p1!
1546: p1.width = 0;
1547:
1548: Rectangle r = p0.union(p1);
1549:
1550: if ((r.y + r.height) < y) {
1551: // CULL: This can happen when you have a component which gets
1552: // replicated multiple times; for example, an output text
1553: // in a data table. The output text DesignBean itself
1554: // is repeated on multiple separate rows. For fast
1555: // modelToView computations for carets and such, I stash
1556: // the box rendered for a DesignBean directly on the
1557: // DesignBean itself. However, this means that it's the
1558: // LAST box created for a DesignBean which has its
1559: // position associated with the DesignBean. Thus, even
1560: // though every single row in the data table could
1561: // match the caret test above (when the caret is actually
1562: // inside the LiveBean, as is the case for editing the
1563: // value attribute of an output text), the position
1564: // lookup for the node will have to choose one particular
1565: // rendering of that node -- it currently uses the last one.
1566: // Therefore, the position we've been assigned may not
1567: // represent positions in this linebox, and if so, cull
1568: // the painting since we could be overwriting foreground
1569: // text on a previous line.
1570: return;
1571: }
1572:
1573: g.fillRect(r.x, r.y, r.width, r.height);
1574: } else {
1575: // different lines
1576: if ((p1.y + p1.height) < y) {
1577: // See comment CULL above
1578: return;
1579: }
1580:
1581: int y2 = y + height;
1582:
1583: // Only paint regions that vertically intersect our box
1584: if (((p0.y + p0.height) > y) && (p0.y < y2)) {
1585: int p0ToMarginWidth = (x + width) - p0.x;
1586: int my1 = p0.y;
1587: int my2 = my1 + p0.height;
1588:
1589: if (my1 < y) {
1590: my1 = y;
1591: }
1592:
1593: if (my2 > y2) {
1594: my2 = y2;
1595: }
1596:
1597: g.fillRect(p0.x, my1, p0ToMarginWidth, my2
1598: - my1);
1599: }
1600:
1601: if ((p0.y + p0.height) != p1.y) {
1602: int my1 = p0.y + p0.height;
1603: int myh = p1.y - (p0.y + p0.height);
1604: int my2 = my1 + myh;
1605:
1606: if ((my2 > y) && (my1 < y2)) {
1607: // Clip to current box size
1608: if (my2 > y2) {
1609: my2 = y2;
1610: }
1611:
1612: if (my1 < y) {
1613: my1 = y;
1614: }
1615:
1616: g.fillRect(x, my1, width, my2 - my1);
1617: }
1618: }
1619:
1620: if (((p1.y + p1.height) > y) && (p1.y < y2)) {
1621: int my1 = p1.y;
1622: int my2 = my1 + p1.height;
1623:
1624: if (my1 < y) {
1625: my1 = y;
1626: }
1627:
1628: if (my2 > y2) {
1629: my2 = y2;
1630: }
1631:
1632: g.fillRect(x, my1, (p1.x - x), my2 - my1);
1633: }
1634: }
1635: }
1636: }
1637: }
1638: }
1639:
1640: /**
1641: * Paint the linebox. This is overiding super because while we
1642: * don't need the clipping in ContainerBox, we also need to
1643: * change background painting.
1644: * The issue is that let's say we have a bunch of text boxes
1645: * in a linebox with backgrounds. Selection-range painting, which
1646: * is done here in LineBoxGroup, needs to happen AFTER the textboxes
1647: * have drawn their backgrounds, but BEFORE they paint their text
1648: * foregrounds!
1649: *
1650: * So TextBoxes and LineBoxes have been modified to not paint their
1651: * backgrounds as part of paint. And here LineBoxGroup will first
1652: * paint its LineBox backgrounds, then the selection, then its
1653: * LineBox foregrounds/text.
1654: */
1655: public void paint(Graphics g, int px, int py) {
1656: px += getX();
1657: py += getY();
1658:
1659: // Box model quirk: my coordinate system is based on the visual
1660: // extents of the boxes - e.g. location and size of the border
1661: // edge. Because of this, when visually traversing the hierarchy,
1662: // I need to add in the margins.
1663: px += leftMargin;
1664: py += effectiveTopMargin;
1665:
1666: if ((Math.abs(px) > 50000) || (Math.abs(py) > 50000)
1667: || (Math.abs(width) > 50000)
1668: || (Math.abs(height) > 50000)) {
1669: // g.setColor(java.awt.Color.RED);
1670: // g.drawString("Fatal Painting Error: box " + this.toString(), 0,
1671: // g.getFontMetrics().getHeight());
1672: // XXX Improving the above error handling.
1673: // TODO Why is actually this state invalid?
1674: ErrorManager.getDefault()
1675: .notify(
1676: ErrorManager.INFORMATIONAL,
1677: new IllegalStateException(
1678: "Fatal painting error:" // NOI18N
1679: + "\nbad box="
1680: + this // NOI18N
1681: + "\nparent of bad box="
1682: + this .getParent())); // NOI18N
1683:
1684: return;
1685: }
1686:
1687: // Paint children backgrounds -- before our own selection
1688: for (int i = 0, n = getBoxCount(); i < n; i++) {
1689: CssBox box = getBox(i);
1690:
1691: if (box.getBoxType() == BoxType.LINEBOX) {
1692: box.paintBackground(g, px, py);
1693: } // other boxes will do their own background painting
1694: }
1695:
1696: paintBackground(g, px, py);
1697:
1698: // Paint children foregrounds
1699: for (int i = 0, n = getBoxCount(); i < n; i++) {
1700: CssBox box = getBox(i);
1701: CssBox positionParent = box.getPositionedBy();
1702:
1703: // XXX is this possible for LineBoxes ? I don't think so...
1704: // Could optimize out. LineBoxes can't be positioned by
1705: // anyone but the LineBoxGroup directly.
1706: if (positionParent != this ) {
1707: // Not positioned by us - need to compute the
1708: // positioning parent's absolute position
1709: box.paint(g, positionParent.getAbsoluteX(),
1710: positionParent.getAbsoluteY());
1711: } else {
1712: box.paint(g, px, py);
1713: }
1714: }
1715:
1716: if (CssBox.paintSpaces) {
1717: g.setColor(Color.CYAN);
1718: g.drawRect(getAbsoluteX(), getAbsoluteY(), width, height);
1719: }
1720: }
1721:
1722: /** Locate the first node that is included in this linebox */
1723: private Node findFirstNode() {
1724: for (int i = 0, n = allBoxes.size(); i < n; i++) {
1725: CssBox box = allBoxes.get(i);
1726:
1727: if (box.getBoxType() == BoxType.TEXT) {
1728: return ((TextBox) box).getNode();
1729: } else if (box.getBoxType() == BoxType.SPACE) {
1730: return ((SpaceBox) box).getNode();
1731: } else if (box.getElement() != null) {
1732: return box.getElement();
1733: }
1734: }
1735:
1736: return null;
1737: }
1738:
1739: /** Locate the last node that is included in this linebox */
1740: private Node findLastNode() {
1741: for (int i = allBoxes.size() - 1; i >= 0; i--) {
1742: CssBox box = allBoxes.get(i);
1743:
1744: if (box.getBoxType() == BoxType.TEXT) {
1745: return ((TextBox) box).getNode();
1746: } else if (box.getBoxType() == BoxType.SPACE) {
1747: return ((SpaceBox) box).getNode();
1748: } else if (box.getElement() != null) {
1749: return box.getElement();
1750: }
1751: }
1752:
1753: return null;
1754: }
1755: }
|