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:
0042: package org.netbeans.modules.visualweb.css2;
0043:
0044: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition;
0045: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition.Bias;
0046: import org.netbeans.modules.visualweb.api.designer.cssengine.CssProvider;
0047: import org.netbeans.modules.visualweb.api.designer.cssengine.CssValue;
0048: import org.netbeans.modules.visualweb.api.designer.cssengine.XhtmlCss;
0049: import org.netbeans.modules.visualweb.api.designer.markup.MarkupService;
0050: import org.netbeans.modules.visualweb.designer.CssUtilities;
0051: import org.netbeans.modules.visualweb.designer.InlineEditor;
0052: import java.awt.Point;
0053: import java.awt.Rectangle;
0054: import java.util.ArrayList;
0055: import java.util.List;
0056:
0057: import org.openide.ErrorManager;
0058: import org.w3c.dom.Element;
0059: import org.w3c.dom.Node;
0060: import org.w3c.dom.NodeList;
0061:
0062: import org.netbeans.modules.visualweb.designer.DesignerUtils;
0063: import org.netbeans.modules.visualweb.designer.WebForm;
0064: import org.netbeans.modules.visualweb.designer.html.HtmlTag;
0065:
0066: /**
0067: * Class responsible for mapping between the DOM (jsp document and
0068: * rendered html fragments) and the view (boxes and pixel coordinates).
0069: * It also handles caret motion. Note that methods which return positions
0070: * never return null, they return Position.NONE in that case.
0071: *
0072: * @todo Handle motion in tables
0073: * @todo Handle motion past iframes, fragment box includes, etc.
0074: * @todo There are two getLastLineboxPosition methods; join them
0075: * @todo Handle the "hidden" flag on boxes, and skip them in
0076: * visual traversal
0077: *
0078: * @author Tor Norbye
0079: */
0080: public final class ModelViewMapper {
0081: // private WebForm webform;
0082:
0083: private ModelViewMapper(WebForm webform) {
0084: // this.webform = webform;
0085: }
0086:
0087: /**
0088: * Given a caret position, return the caret position to its left. This
0089: * may not actually be the left on the screen, e.g. if you press left at the
0090: * beginning of a line you may end up on the right side of the previous
0091: * line.
0092: * @param pos The caret position in the DOM
0093: * @return The next visual position to the "left" of pos, or Position.NONE if
0094: * there is no such position in the document (e.g. when you're at the
0095: * first visual position in the document.)
0096: */
0097: // public static Position computeArrowLeft(WebForm webform, Position sourcePos) {
0098: public static DomPosition computeArrowLeft(WebForm webform,
0099: DomPosition sourcePos) {
0100:
0101: // assert !sourcePos.isRendered();
0102: // if (MarkupService.isRenderedNode(sourcePos.getNode())) {
0103: if (sourcePos.isRenderedPosition()) {
0104: ErrorManager.getDefault().notify(
0105: ErrorManager.INFORMATIONAL,
0106: new IllegalArgumentException(
0107: "Node is expected to be not rendered, node="
0108: + sourcePos.getNode())); // NOI18N
0109: }
0110:
0111: // Position pos = sourcePos.getRenderedPosition();
0112: DomPosition pos = sourcePos.getRenderedPosition();
0113:
0114: // TODO: in a table, use special case such that we handle the
0115: // first-cell-in-a-row scenario correctly
0116: LineBox lb = findLineBox(webform.getPane().getPageBox(), pos);
0117:
0118: if (lb == null) {
0119: // Caret positions should always be in a linebox unless we're inline editing;
0120: // in inline editing mode we can delete backwards until we have no caret position
0121: // XXX #104463 This doesn't seem to be correct here.
0122: // assert webform.getManager().isInlineEditing() : pos;
0123:
0124: // return Position.NONE;
0125: return DomPosition.NONE;
0126: }
0127:
0128: // Position p = findPrevPosition(lb, pos, webform.getManager().getInlineEditor());
0129: DomPosition p = findPrevPosition(lb, pos, webform.getManager()
0130: .getInlineEditor());
0131:
0132: // if (p != Position.NONE) {
0133: if (p != DomPosition.NONE) {
0134:
0135: // if (p.isRendered()) {
0136: // if (MarkupService.isRenderedNode(p.getNode())) {
0137: if (p.isRenderedPosition()) {
0138: p = p.getSourcePosition();
0139: }
0140: }
0141:
0142: return p;
0143: }
0144:
0145: /**
0146: * Given a caret position, return the caret position to its right. This
0147: * may not actually be the right on the screen, e.g. if you press right at the
0148: * end of a line you may end up on the left side of the next
0149: * line.
0150: * @param pos The caret position in the DOM
0151: * @return The next visual position to the "right" of pos, or Position.NONE if
0152: * there is no such position in the document (e.g. when you're at the
0153: * last visual position in the document.)
0154: */
0155: // public static Position computeArrowRight(WebForm webform, Position sourcePos) {
0156: public static DomPosition computeArrowRight(WebForm webform,
0157: DomPosition sourcePos) {
0158: // assert !sourcePos.isRendered();
0159: // if (MarkupService.isRenderedNode(sourcePos.getNode())) {
0160: if (sourcePos.isRenderedPosition()) {
0161: ErrorManager.getDefault().notify(
0162: ErrorManager.INFORMATIONAL,
0163: new IllegalArgumentException(
0164: "Node is expected to be not rendered, node="
0165: + sourcePos.getNode())); // NOI18N
0166: }
0167:
0168: // Position pos = sourcePos.getRenderedPosition();
0169: DomPosition pos = sourcePos.getRenderedPosition();
0170:
0171: // TODO: in a table, use special case such that we handle the
0172: // last-cell-in-a-row scenario correctly
0173: LineBox lb = findLineBox(webform.getPane().getPageBox(), pos);
0174:
0175: if (lb == null) {
0176: // assert false : pos; // Caret positions should always be in a linebox!!
0177:
0178: // return Position.NONE;
0179: return DomPosition.NONE;
0180: }
0181:
0182: // Position p = findNextPosition(lb, pos, webform.getManager().getInlineEditor());
0183: DomPosition p = findNextPosition(lb, pos, webform.getManager()
0184: .getInlineEditor());
0185:
0186: // if (p != Position.NONE) {
0187: if (p != DomPosition.NONE) {
0188:
0189: // if (p.isRendered()) {
0190: // if (MarkupService.isRenderedNode(p.getNode())) {
0191: if (p.isRenderedPosition()) {
0192: p = p.getSourcePosition();
0193: }
0194: }
0195:
0196: return p;
0197: }
0198:
0199: /**
0200: * Given a caret position, return the caret position to "above" it; e.g. the
0201: * position we should move the caret to if the user presses the up arrow key.
0202: * @param pos The caret position in the DOM
0203: * @return The next visual position above the given pos, or Position.NONE if
0204: * there is no such position in the document (e.g. when you're on the first line
0205: * in the document.)
0206: */
0207: // public static Position computeArrowUp(WebForm webform, Position sourcePos) {
0208: public static DomPosition computeArrowUp(WebForm webform,
0209: DomPosition sourcePos) {
0210: // TODO: If in a table, figure out the current position (I could stash these right
0211: // on the cellboxes), subtract one, and if still within the table, look for the first
0212: // cell root (e.g. skip spans) and use that. Otherwise (if we're outside of the table),
0213: // use the table itself as an origin, then do the normal linebox search.
0214: // assert !sourcePos.isRendered();
0215: // if (MarkupService.isRenderedNode(sourcePos.getNode())) {
0216: if (sourcePos.isRenderedPosition()) {
0217: ErrorManager.getDefault().notify(
0218: ErrorManager.INFORMATIONAL,
0219: new IllegalStateException(
0220: "Node is expected to be not rendered, node="
0221: + sourcePos.getNode())); // NOI18N
0222: }
0223:
0224: // Position pos = sourcePos.getRenderedPosition();
0225: DomPosition pos = sourcePos.getRenderedPosition();
0226:
0227: LineBox lb = findLineBox(webform.getPane().getPageBox(), pos);
0228:
0229: if (lb == null) {
0230: // assert false : pos; // Caret positions should always be in a linebox!!
0231:
0232: // return Position.NONE;
0233: return DomPosition.NONE;
0234: }
0235:
0236: // DesignerCaret caret = webform.getPane().getCaret();
0237: // assert caret != null;
0238: if (!webform.getPane().hasCaret()) {
0239: ErrorManager.getDefault().notify(
0240: ErrorManager.INFORMATIONAL,
0241: new IllegalStateException(
0242: "Pane doesn't have caret!"));
0243: return DomPosition.NONE;
0244: }
0245:
0246: // Point magicPosition = caret.getMagicCaretPosition();
0247: Point magicPosition = webform.getPane().getCaretMagicPosition();
0248:
0249: LineBox prev = lb;
0250:
0251: while (true) {
0252: prev = findPrevLineBox(prev);
0253:
0254: if (prev == null) {
0255: // return Position.NONE;
0256: return DomPosition.NONE;
0257: }
0258:
0259: // Position p = prev.computePosition(magicPosition.x - prev.getAbsoluteX());
0260: DomPosition p = prev.computePosition(magicPosition.x
0261: - prev.getAbsoluteX());
0262:
0263: // if (DesignerUtils.checkPosition(p, false, /*webform*/webform.getManager().getInlineEditor()) != Position.NONE) {
0264: // if (isValidPosition(p, false, /*webform*/webform.getManager().getInlineEditor())) {
0265: if (isValidPosition(webform, p, false, /*webform*/webform
0266: .getManager().getInlineEditor())) {
0267: // if (p != Position.NONE) {
0268: if (p != DomPosition.NONE) {
0269:
0270: // if (p.isRendered()) {
0271: // if (MarkupService.isRenderedNode(p.getNode())) {
0272: if (p.isRenderedPosition()) {
0273: p = p.getSourcePosition();
0274: }
0275: }
0276:
0277: return p;
0278: }
0279: }
0280: }
0281:
0282: /**
0283: * Given a caret position, return the caret position to "below" it; e.g. the
0284: * position we should move the caret to if the user presses the down arrow key.
0285: * @param pos The caret position in the DOM
0286: * @return The next visual position below the given pos, or Position.NONE if
0287: * there is no such position in the document (e.g. when you're on the last line
0288: * in the document.)
0289: */
0290: // public static Position computeArrowDown(WebForm webform, Position sourcePos) {
0291: public static DomPosition computeArrowDown(WebForm webform,
0292: DomPosition sourcePos) {
0293: // TODO: If in a table, figure out the current position (I could stash these right
0294: // on the cellboxes), add one, and if still within the table, look for the first
0295: // cell root (e.g. skip spans) and use that. Otherwise (if we're outside of the table),
0296: // use the table itself as an origin, then do the normal linebox search.
0297: // assert !sourcePos.isRendered();
0298: // if (MarkupService.isRenderedNode(sourcePos.getNode())) {
0299: if (sourcePos.isRenderedPosition()) {
0300: ErrorManager.getDefault().notify(
0301: ErrorManager.INFORMATIONAL,
0302: new IllegalStateException(
0303: "Node is expected to be not rendered, node="
0304: + sourcePos.getNode())); // NOI18N
0305: }
0306:
0307: // Position pos = sourcePos.getRenderedPosition();
0308: DomPosition pos = sourcePos.getRenderedPosition();
0309:
0310: LineBox lb = findLineBox(webform.getPane().getPageBox(), pos);
0311:
0312: if (lb == null) {
0313: // assert false : pos; // Caret positions should always be in a linebox!!
0314:
0315: // return Position.NONE;
0316: return DomPosition.NONE;
0317: }
0318:
0319: // DesignerCaret caret = webform.getPane().getCaret();
0320: // assert caret != null;
0321: if (!webform.getPane().hasCaret()) {
0322: ErrorManager.getDefault().notify(
0323: ErrorManager.INFORMATIONAL,
0324: new IllegalStateException(
0325: "Pane doesn't have caret!"));
0326: return DomPosition.NONE;
0327: }
0328:
0329: // Point magicPosition = caret.getMagicCaretPosition();
0330: Point magicPosition = webform.getPane().getCaretMagicPosition();
0331:
0332: LineBox next = lb;
0333:
0334: while (true) {
0335: next = findNextLineBox(next);
0336:
0337: if (next == null) {
0338: // return Position.NONE;
0339: return DomPosition.NONE;
0340: }
0341:
0342: // Position p = next.computePosition(magicPosition.x - next.getAbsoluteX());
0343: DomPosition p = next.computePosition(magicPosition.x
0344: - next.getAbsoluteX());
0345:
0346: // if (DesignerUtils.checkPosition(p, false, /*webform*/webform.getManager().getInlineEditor()) != Position.NONE) {
0347: // if (isValidPosition(p, false, /*webform*/webform.getManager().getInlineEditor())) {
0348: if (isValidPosition(webform, p, false, /*webform*/webform
0349: .getManager().getInlineEditor())) {
0350: // if (p != Position.NONE) {
0351: if (p != DomPosition.NONE) {
0352:
0353: // if (p.isRendered()) {
0354: // if (MarkupService.isRenderedNode(p.getNode())) {
0355: if (p.isRenderedPosition()) {
0356: p = p.getSourcePosition();
0357: }
0358: }
0359:
0360: return p;
0361: }
0362: }
0363: }
0364:
0365: /**
0366: * Given a LineBox and a position in that line box, find the FIRST acceptable caret position.
0367: * Note that the position may be in a different line box (this happens if the
0368: * position you passed in was the last caret position in the linebox).
0369: */
0370: // private static Position findNextPosition(LineBox lb, Position pos, InlineEditor inlineEditor) {
0371: private static DomPosition findNextPosition(LineBox lb,
0372: DomPosition pos, InlineEditor inlineEditor) {
0373: // assert pos.isRendered();
0374: // if (!MarkupService.isRenderedNode(pos.getNode())) {
0375: if (pos.isSourcePosition()) {
0376: ErrorManager.getDefault().notify(
0377: ErrorManager.INFORMATIONAL,
0378: new IllegalArgumentException(
0379: "Node is expected to be rendered, node="
0380: + pos.getNode())); // NOI18N
0381: }
0382:
0383: Node node = pos.getNode();
0384: int offset = pos.getOffset();
0385:
0386: if (node.getNodeType() == Node.ELEMENT_NODE) {
0387: NodeList nl = node.getChildNodes();
0388:
0389: if ((offset >= nl.getLength())
0390: || ((pos.getBias() == Bias.BACKWARD) && (offset > 0))) {
0391: node = nl.item(offset - 1);
0392: offset = 1;
0393: } else {
0394: node = nl.item(offset);
0395: offset = 0;
0396: }
0397: }
0398:
0399: CssBox box = null;
0400:
0401: // Iterate over the line box contents. When you find the box containing the
0402: // current position, see if it contains the next position, and if so, return it.
0403: int i = 0;
0404:
0405: // Iterate over the line box contents. When you find the box containing the
0406: // current position, see if it contains the next position, and if so, return it.
0407: int n = lb.getBoxCount();
0408:
0409: for (; i < n; i++) {
0410: box = lb.getBox(i);
0411:
0412: if (box.getBoxType() == BoxType.TEXT) {
0413: TextBox tb = (TextBox) box;
0414:
0415: if ((tb.getNode() == node) && (
0416: /* This doesn't work when the position is somewhere in the DOM I'm
0417: not displaying, like in a region of space characters. Since I search
0418: the dom forwards however, I don't need this extra check.
0419: tb.getDomStartOffset() <= offset && */
0420: offset <= tb.getDomEndOffset())) {
0421: // Position p = tb.getNext(pos);
0422: DomPosition p = tb.getNext(pos);
0423:
0424: // if (p != Position.NONE) {
0425: if (p != DomPosition.NONE) {
0426: return p;
0427: }
0428:
0429: break;
0430: }
0431: } else if (box.getBoxType() == BoxType.SPACE) {
0432: SpaceBox tb = (SpaceBox) box;
0433:
0434: if ((tb.getNode() == node) && (
0435: /* This doesn't work when the position is somewhere in the DOM I'm
0436: not displaying, like in a region of space characters. Since I search
0437: the dom forwards however, I don't need this extra check.
0438: tb.getDomStartOffset() <= offset && */
0439: offset <= tb.getDomEndOffset())) {
0440: // Position p = tb.getNext(pos);
0441: DomPosition p = tb.getNext(pos);
0442:
0443: // if (p != Position.NONE) {
0444: if (p != DomPosition.NONE) {
0445: return p;
0446: }
0447:
0448: break;
0449: }
0450: } else if (box.getBoxType() == BoxType.LINEBREAK) {
0451: break;
0452: // } else if (getElement(box) == node) {
0453: } else if (getComponentRootElementParentForCssBox(box) == node) {
0454: if (offset == 0) {
0455: // We can resolve this one quickly! Just pick position after
0456: // return Position.create(getElement(box).getSource(), true);
0457: // return Position.create(MarkupService.getSourceElementForElement(getElement(box)), true);
0458: // return Position.create(MarkupService.getSourceElementForElement(
0459: // getComponentRootElementParentForCssBox(box)), true);
0460: return lb
0461: .getWebForm()
0462: .createDomPosition(
0463: MarkupService
0464: .getSourceElementForElement(getComponentRootElementParentForCssBox(box)),
0465: true);
0466: }
0467:
0468: if (i >= (n - 1)) {
0469: // We've walked out of the last textbox/spacebox etc in the line
0470: // so jump to the next line
0471: return getFirstPosNextLine(lb, inlineEditor);
0472: } else {
0473: return findFirstLineboxPosition(lb, i + 1,
0474: inlineEditor);
0475: }
0476:
0477: /*
0478: } else if (pos.isInside(box.getSourceElement())) {
0479: break;
0480: */
0481: }
0482: }
0483:
0484: // assert i != n; // LineBox should have contained box; if not input arg was invalid
0485: if (i == n) {
0486: ErrorManager.getDefault().notify(
0487: ErrorManager.INFORMATIONAL,
0488: new IllegalStateException(
0489: "Next position not found, lb=" + lb
0490: + ", pos=" + pos)); // NOI18N
0491: // return Position.NONE;
0492: return DomPosition.NONE;
0493: }
0494:
0495: while (true) { // May have to try repeatedly to skip JSF fragment linebox contents
0496:
0497: if (i == n) {
0498: return getFirstPosNextLine(lb, inlineEditor);
0499: }
0500:
0501: // If there is no next box, or if the next box is a linebreak box,
0502: // Jump to the Next Line lb.getBox(i)
0503: if ((i >= (n - 1)) || // if there is no next box
0504: (lb.getBox(i + 1).getBoxType() == BoxType.LINEBREAK)) {
0505: // Jump to the next line: Find Next Line Box and if not null,
0506: // Find First Position in it and return that.
0507: return getFirstPosNextLine(lb, inlineEditor);
0508: }
0509:
0510: // If the next box is a space box or a text box, ask it for the next position
0511: // (e.g. skipping its first position) and return it.
0512: CssBox next = lb.getBox(i + 1);
0513:
0514: if (next.getBoxType() == BoxType.TEXT) {
0515: // We know it SHOULD have one position since all text and space boxes
0516: // must have at least one character
0517: TextBox tb = (TextBox) next;
0518: // Position p = tb.getNext(tb.getFirstPosition());
0519: DomPosition p = tb.getNext(tb.getFirstPosition());
0520:
0521: // if (DesignerUtils.checkPosition(p, false, inlineEditor) != Position.NONE) {
0522: // if (isValidPosition(p, false, inlineEditor)) {
0523: if (isValidPosition(lb.getWebForm(), p, false,
0524: inlineEditor)) {
0525: return p;
0526: } else {
0527: // else it's probably a JSF component
0528: i++;
0529:
0530: continue;
0531: }
0532: } else if (next.getBoxType() == BoxType.SPACE) {
0533: SpaceBox tb = (SpaceBox) next;
0534: // Position p = tb.getNext(tb.getFirstPosition());
0535: DomPosition p = tb.getNext(tb.getFirstPosition());
0536:
0537: // if (DesignerUtils.checkPosition(p, false, inlineEditor) != Position.NONE) {
0538: // if (isValidPosition(p, false, inlineEditor)) {
0539: if (isValidPosition(lb.getWebForm(), p, false,
0540: inlineEditor)) {
0541: return p;
0542: } else {
0543: // else it's probably a JSF component
0544: i++;
0545:
0546: continue;
0547: }
0548: }
0549:
0550: // Otherwise the next box must be an inline box (like an image, or a
0551: // button), so skip it. If there are no more boxes in the LineBox after
0552: // this box, we should jump to the next line.
0553: i += 2;
0554:
0555: if (i >= n) {
0556: // We've walked out of the last textbox/spacebox etc in the line
0557: // so jump to the next line
0558: return getFirstPosNextLine(lb, inlineEditor);
0559: } else {
0560: return findFirstLineboxPosition(lb, i, inlineEditor);
0561: }
0562: }
0563: }
0564:
0565: /**
0566: * Given a LineBox, compute the NEXT linebox and return its first
0567: * position. Used as part of getNextPosition for various cases.
0568: */
0569: // private static Position getFirstPosNextLine(LineBox lb, InlineEditor inlineEditor) {
0570: private static DomPosition getFirstPosNextLine(LineBox lb,
0571: InlineEditor inlineEditor) {
0572: // Jump to the next line: Find Next Line Box and if not null,
0573: // Find First Position in it and return that.
0574: LineBox nextLine = findNextLineBox(lb);
0575:
0576: if (nextLine != null) {
0577: return findFirstLineboxPosition(nextLine, 0, inlineEditor);
0578: } else {
0579: // return Position.NONE;
0580: return DomPosition.NONE;
0581: }
0582: }
0583:
0584: /**
0585: * Given a LineBox, compute the PREVIOUS linebox and return its last.
0586: * position. Used as part of getPrevPosition for various cases.
0587: * @param inlineEditor (XXX suspicious) InlineEditor which is active in the page (or <code>null</code>).
0588: */
0589: // private static Position getLastPosPrevLine(LineBox lb, InlineEditor inlineEditor) {
0590: private static DomPosition getLastPosPrevLine(LineBox lb,
0591: InlineEditor inlineEditor) {
0592: // Jump to the previous line:
0593: // Find Previous Line Box and if not null, Find Last Position in it
0594: // and return that.
0595: LineBox prevLine = findPrevLineBox(lb);
0596:
0597: if (prevLine != null) {
0598: return findLastLineboxPosition(prevLine, inlineEditor);
0599: } else {
0600: // return Position.NONE;
0601: return DomPosition.NONE;
0602: }
0603: }
0604:
0605: /**
0606: * Analogous to Find Next Position but move in the opposite direction.
0607: * Note that the position may be in a different line box (this happens if the position
0608: * you passed in was the first caret position in the linebox).
0609: * @param inlineEditor (XXX suspicious) InlineEditor which is active in the page (or <code>null</code>).
0610: */
0611: // private static Position findPrevPosition(LineBox lb, Position pos, InlineEditor inlineEditor) {
0612: private static DomPosition findPrevPosition(LineBox lb,
0613: DomPosition pos, InlineEditor inlineEditor) {
0614: // assert pos.isRendered();
0615: // if (!MarkupService.isRenderedNode(pos.getNode())) {
0616: if (pos.isSourcePosition()) {
0617: ErrorManager.getDefault().notify(
0618: ErrorManager.INFORMATIONAL,
0619: new IllegalArgumentException(
0620: "Node is expected to be rendered, node="
0621: + pos.getNode())); // NOI18N
0622: }
0623:
0624: Node node = pos.getNode();
0625: int offset = pos.getOffset();
0626:
0627: if (node.getNodeType() == Node.ELEMENT_NODE) {
0628: NodeList nl = node.getChildNodes();
0629:
0630: if ((offset >= nl.getLength())
0631: || ((pos.getBias() == Bias.BACKWARD) && (offset > 0))) {
0632: node = nl.item(offset - 1);
0633: offset = 1;
0634: } else {
0635: node = nl.item(offset);
0636: offset = 0;
0637: }
0638: }
0639:
0640: CssBox box = null;
0641:
0642: // Iterate over the line box contents. When you find the box containing the
0643: // current position, see if it contains the previous position, and if so, return it.
0644: int i = 0;
0645:
0646: // Iterate over the line box contents. When you find the box containing the
0647: // current position, see if it contains the previous position, and if so, return it.
0648: int n = lb.getBoxCount();
0649:
0650: for (; i < n; i++) {
0651: box = lb.getBox(i);
0652:
0653: if (box.getBoxType() == BoxType.TEXT) {
0654: TextBox tb = (TextBox) box;
0655:
0656: if ((tb.getNode() == node) && (
0657: /* This doesn't work when the position is somewhere in the DOM I'm
0658: not displaying, like in a region of space characters. Since I search
0659: the dom forwards however, I don't need this extra check.
0660: tb.getDomStartOffset() <= offset && */
0661: offset <= tb.getDomEndOffset())) {
0662: // Position p = tb.getPrev(pos);
0663: DomPosition p = tb.getPrev(pos);
0664:
0665: // if (p != Position.NONE) {
0666: if (p != DomPosition.NONE) {
0667: return p;
0668: }
0669:
0670: break;
0671: }
0672: } else if (box.getBoxType() == BoxType.SPACE) {
0673: SpaceBox tb = (SpaceBox) box;
0674:
0675: if ((tb.getNode() == node) && (
0676: /* This doesn't work when the position is somewhere in the DOM I'm
0677: not displaying, like in a region of space characters. Since I search
0678: the dom forwards however, I don't need this extra check.
0679: tb.getDomStartOffset() <= offset && */
0680: offset <= tb.getDomEndOffset())) {
0681: // Position p = tb.getPrev(pos);
0682: DomPosition p = tb.getPrev(pos);
0683:
0684: // if (p != Position.NONE) {
0685: if (p != DomPosition.NONE) {
0686: return p;
0687: }
0688:
0689: break;
0690: }
0691: } else if (box.getBoxType() == BoxType.LINEBREAK) {
0692: break;
0693: // } else if (getElement(box) == node) {
0694: } else if (getComponentRootElementParentForCssBox(box) == node) {
0695: if (offset == 1) {
0696: // We can resolve this one quickly! Just pick position before
0697: // return Position.create(getElement(box).getSource(), false);
0698: // return Position.create(MarkupService.getSourceElementForElement(getElement(box)), false);
0699: // return Position.create(MarkupService.getSourceElementForElement(
0700: // getComponentRootElementParentForCssBox(box)), false);
0701: return lb
0702: .getWebForm()
0703: .createDomPosition(
0704: MarkupService
0705: .getSourceElementForElement(getComponentRootElementParentForCssBox(box)),
0706: false);
0707: }
0708:
0709: if (i == 0) {
0710: // We've walked out of the first textbox/spacebox etc in the line
0711: // so jump to the previous line
0712: return getLastPosPrevLine(lb, inlineEditor);
0713: } else {
0714: return findLastLineboxPosition(lb, i - 1,
0715: inlineEditor);
0716: }
0717:
0718: /*
0719: } else if (pos.isInside(box.getSourceElement())) {
0720: break;
0721: */
0722: }
0723: }
0724:
0725: // assert i != n; // LineBox should have contained box; if not input arg was invalid
0726: if (i == n) {
0727: ErrorManager.getDefault().notify(
0728: ErrorManager.INFORMATIONAL,
0729: new IllegalStateException(
0730: "Previous position not found, lb=" + lb
0731: + ", pos=" + pos)); // NOI18N
0732: // return Position.NONE;
0733: return DomPosition.NONE;
0734: }
0735:
0736: while (true) {
0737: // If there is no previous box, Jump to the Previous Line
0738: if (i == 0) {
0739: // Jump to the previous line:
0740: return getLastPosPrevLine(lb, inlineEditor);
0741: }
0742:
0743: // If the previous box is a space box or a text box, ask it for the previous position
0744: // (e.g. skipping its last position) and return it.
0745: CssBox prev = lb.getBox(i - 1);
0746:
0747: if (prev.getBoxType() == BoxType.TEXT) {
0748: // We know it SHOULD have one position since all text and space boxes
0749: // must have at least one character
0750: TextBox tb = (TextBox) prev;
0751: // Position p = tb.getPrev(tb.getLastPosition());
0752: DomPosition p = tb.getPrev(tb.getLastPosition());
0753:
0754: // if (DesignerUtils.checkPosition(p, false, inlineEditor) != Position.NONE) {
0755: // if (isValidPosition(p, false, inlineEditor)) {
0756: if (isValidPosition(lb.getWebForm(), p, false,
0757: inlineEditor)) {
0758: return p;
0759: } else {
0760: // else it's probably a JSF component
0761: i--;
0762:
0763: continue;
0764: }
0765: } else if (prev.getBoxType() == BoxType.SPACE) {
0766: SpaceBox tb = (SpaceBox) prev;
0767: // Position p = tb.getPrev(tb.getLastPosition());
0768: DomPosition p = tb.getPrev(tb.getLastPosition());
0769:
0770: // if (DesignerUtils.checkPosition(p, false, inlineEditor) != Position.NONE) {
0771: // if (isValidPosition(p, false, inlineEditor)) {
0772: if (isValidPosition(lb.getWebForm(), p, false,
0773: inlineEditor)) {
0774: return p;
0775: } else {
0776: // else it's probably a JSF component
0777: i--;
0778:
0779: continue;
0780: }
0781: }
0782:
0783: // Otherwise the previous box must be an inline box (like an image, or a
0784: // button), so skip it. If this was the first box in the linebox, we should
0785: // jump to the previous line. (can I combine these too?
0786: i--;
0787:
0788: if (i < 0) {
0789: // We've walked out of the first textbox/spacebox etc in the line
0790: // so jump to the previous line
0791: return getLastPosPrevLine(lb, inlineEditor);
0792: } else {
0793: return findLastLineboxPosition(lb, i, inlineEditor);
0794: }
0795: }
0796: }
0797:
0798: /**
0799: * Given a Line Box, return the first position in the line box
0800: * starting at the given box offset. Pass in 0 to get the first
0801: * position in the line box.
0802: * @param lb The line box
0803: * @return The first legal caret position in the line box
0804: */
0805: // private static Position findFirstLineboxPosition(LineBox lb, int index, InlineEditor inlineEditor) {
0806: private static DomPosition findFirstLineboxPosition(LineBox lb,
0807: int index, InlineEditor inlineEditor) {
0808: if (lb.getBoxCount() <= index) {
0809: // assert false : lb;
0810:
0811: // return Position.NONE;
0812: return DomPosition.NONE;
0813: }
0814:
0815: while (true) {
0816: CssBox box = lb.getBox(index);
0817:
0818: if (box.getBoxType() == BoxType.TEXT) {
0819: TextBox tb = (TextBox) box;
0820: // Position pos = tb.getFirstPosition();
0821: DomPosition pos = tb.getFirstPosition();
0822:
0823: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
0824: // if (isValidPosition(pos, false, inlineEditor)) {
0825: if (isValidPosition(lb.getWebForm(), pos, false,
0826: inlineEditor)) {
0827: return pos;
0828: }
0829: } else if (box.getBoxType() == BoxType.SPACE) {
0830: SpaceBox tb = (SpaceBox) box;
0831: // Position pos = tb.getFirstPosition();
0832: DomPosition pos = tb.getFirstPosition();
0833:
0834: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
0835: // if (isValidPosition(pos, false, inlineEditor)) {
0836: if (isValidPosition(lb.getWebForm(), pos, false,
0837: inlineEditor)) {
0838: return pos;
0839: }
0840: } else {
0841: // assert that this is a simple inline, noncontainer box,
0842: // such as an image, a StringBox, an iframe, etc.
0843: // Position pos = Position.create(box.getSourceElement(), false);
0844: DomPosition pos = lb.getWebForm().createDomPosition(
0845: box.getSourceElement(), false);
0846:
0847: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
0848: // if (isValidPosition(pos, false, inlineEditor)) {
0849: if (isValidPosition(lb.getWebForm(), pos, false,
0850: inlineEditor)) {
0851: return pos;
0852: }
0853: }
0854:
0855: index++;
0856:
0857: if (index == lb.getBoxCount()) {
0858: while (true) {
0859: lb = findNextLineBox(lb);
0860:
0861: if (lb == null) {
0862: // return Position.NONE;
0863: return DomPosition.NONE;
0864: }
0865:
0866: index = 0;
0867:
0868: if (index < lb.getBoxCount()) {
0869: break;
0870: }
0871: }
0872: }
0873: }
0874: }
0875:
0876: /**
0877: * Given a Line Box, return the last position in the line box.
0878: * @param lb The line box
0879: * @param inlineEditor (XXX suspicious) InlineEditor which is active in the page (or <code>null</code>).
0880: * @return The last legal caret position in the line box
0881: */
0882: // private static Position findLastLineboxPosition(LineBox lb, int index, InlineEditor inlineEditor) {
0883: private static DomPosition findLastLineboxPosition(LineBox lb,
0884: int index, InlineEditor inlineEditor) {
0885: if (lb.getBoxCount() <= index) {
0886: assert false : lb;
0887:
0888: // return Position.NONE;
0889: return DomPosition.NONE;
0890: }
0891:
0892: while (true) {
0893: CssBox box = lb.getBox(index);
0894:
0895: if (box.getBoxType() == BoxType.TEXT) {
0896: TextBox tb = (TextBox) box;
0897: // Position pos = tb.getLastPosition();
0898: DomPosition pos = tb.getLastPosition();
0899:
0900: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
0901: // if (isValidPosition(pos, false, inlineEditor)) {
0902: if (isValidPosition(lb.getWebForm(), pos, false,
0903: inlineEditor)) {
0904: return pos;
0905: }
0906: } else if (box.getBoxType() == BoxType.SPACE) {
0907: SpaceBox tb = (SpaceBox) box;
0908: // Position pos = tb.getLastPosition();
0909: DomPosition pos = tb.getLastPosition();
0910:
0911: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
0912: // if (isValidPosition(pos, false, inlineEditor)) {
0913: if (isValidPosition(lb.getWebForm(), pos, false,
0914: inlineEditor)) {
0915: return pos;
0916: }
0917: } else {
0918: // assert that this is a simple inline, noncontainer box,
0919: // such as an image, a StringBox, an iframe, etc.
0920: if (index > 0) {
0921: CssBox prev = lb.getBox(index - 1);
0922:
0923: if (prev.getBoxType() == BoxType.TEXT) {
0924: TextBox tb = (TextBox) prev;
0925: // Position pos = tb.getLastPosition();
0926: DomPosition pos = tb.getLastPosition();
0927:
0928: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
0929: // if (isValidPosition(pos, false, inlineEditor)) {
0930: if (isValidPosition(lb.getWebForm(), pos,
0931: false, inlineEditor)) {
0932: return pos;
0933: }
0934: } else if (prev.getBoxType() == BoxType.SPACE) {
0935: SpaceBox tb = (SpaceBox) prev;
0936: // Position pos = tb.getLastPosition();
0937: DomPosition pos = tb.getLastPosition();
0938:
0939: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
0940: // if (isValidPosition(pos, false, inlineEditor)) {
0941: if (isValidPosition(lb.getWebForm(), pos,
0942: false, inlineEditor)) {
0943: return pos;
0944: }
0945: }
0946: }
0947:
0948: // Position pos = Position.create(box.getSourceElement(), false);
0949: DomPosition pos = lb.getWebForm().createDomPosition(
0950: box.getSourceElement(), false);
0951:
0952: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
0953: // if (isValidPosition(pos, false, inlineEditor)) {
0954: if (isValidPosition(lb.getWebForm(), pos, false,
0955: inlineEditor)) {
0956: return pos;
0957: }
0958: }
0959:
0960: index--; // Probably a JSF component
0961:
0962: if (index < 0) {
0963: while (true) {
0964: lb = findPrevLineBox(lb);
0965:
0966: if (lb == null) {
0967: // return Position.NONE;
0968: return DomPosition.NONE;
0969: }
0970:
0971: index = lb.getBoxCount() - 1;
0972:
0973: if (index >= 0) {
0974: break;
0975: }
0976: }
0977: }
0978: }
0979: }
0980:
0981: /**
0982: * Given a Line Box, return the last position in the line box.
0983: * @todo XXX This needs to be combined with findLastLineboxPosition(LineBox,int)!!!
0984: * @param lb The line box
0985: * @param inlineEditor (XXX suspicious) InlineEditor which is active in the page (or <code>null</code>).
0986: * @return The last legal caret position in the line box
0987: */
0988: // private static Position findLastLineboxPosition(LineBox lb, InlineEditor inlineEditor) {
0989: private static DomPosition findLastLineboxPosition(LineBox lb,
0990: InlineEditor inlineEditor) {
0991: if (lb.getBoxCount() == 0) {
0992: // assert false : lb;
0993:
0994: // return Position.NONE;
0995: return DomPosition.NONE;
0996: }
0997:
0998: int index = lb.getBoxCount() - 1;
0999:
1000: while (true) {
1001: CssBox box = lb.getBox(index);
1002:
1003: if (box.getBoxType() == BoxType.LINEBREAK) {
1004: // Position pos = Position.create(box.getSourceElement(), false);
1005: DomPosition pos = lb.getWebForm().createDomPosition(
1006: box.getSourceElement(), false);
1007:
1008: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
1009: // if (isValidPosition(pos, false, inlineEditor)) {
1010: if (isValidPosition(lb.getWebForm(), pos, false,
1011: inlineEditor)) {
1012: return pos;
1013: }
1014: }
1015:
1016: if (box.getBoxType() == BoxType.TEXT) {
1017: TextBox tb = (TextBox) box;
1018: // Position pos = tb.getLastPosition();
1019: DomPosition pos = tb.getLastPosition();
1020:
1021: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
1022: // if (isValidPosition(pos, false, inlineEditor)) {
1023: if (isValidPosition(lb.getWebForm(), pos, false,
1024: inlineEditor)) {
1025: return pos;
1026: }
1027: } else if (box.getBoxType() == BoxType.SPACE) {
1028: SpaceBox tb = (SpaceBox) box;
1029: // Position pos = tb.getLastPosition();
1030: DomPosition pos = tb.getLastPosition();
1031:
1032: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
1033: // if (isValidPosition(pos, false, inlineEditor)) {
1034: if (isValidPosition(lb.getWebForm(), pos, false,
1035: inlineEditor)) {
1036: return pos;
1037: }
1038: } else {
1039: // Position pos = Position.create(box.getSourceElement(), true);
1040: DomPosition pos = lb.getWebForm().createDomPosition(
1041: box.getSourceElement(), true);
1042:
1043: // if (DesignerUtils.checkPosition(pos, false, inlineEditor) != Position.NONE) {
1044: // if (isValidPosition(pos, false, inlineEditor)) {
1045: if (isValidPosition(lb.getWebForm(), pos, false,
1046: inlineEditor)) {
1047: return pos;
1048: }
1049: }
1050:
1051: // It was probably a JSF component
1052: index--;
1053:
1054: if (index < 0) {
1055: while (true) {
1056: lb = findPrevLineBox(lb);
1057:
1058: if (lb == null) {
1059: // return Position.NONE;
1060: return DomPosition.NONE;
1061: }
1062:
1063: index = lb.getBoxCount() - 1;
1064:
1065: if (index >= 0) {
1066: break;
1067: }
1068: }
1069: }
1070: }
1071: }
1072:
1073: /**
1074: * Given a box, find the next linebox in flow searching forwards.
1075: * @param box The box to start the search from (which may be
1076: * inside a linebox. The containing linebox will not be returned
1077: * as the next linebox.
1078: * @return The next linebox in flow.
1079: */
1080: private static LineBox findNextLineBox(CssBox box) {
1081: if ((box != null) && (box.getBoxType() != BoxType.LINEBOX)
1082: && box.getParent() instanceof LineBoxGroup) {
1083: // The box is already managed as part of a line box group
1084: // See which line box it's in.
1085: CssBox parent = box.getParent();
1086:
1087: for (int j = 0, n = parent.getBoxCount(); j < n; j++) {
1088: CssBox linebox = parent.getBox(j);
1089:
1090: if (linebox.getBoxType() != BoxType.LINEBOX) {
1091: continue;
1092: }
1093:
1094: for (int k = 0, m = linebox.getBoxCount(); k < m; k++) {
1095: CssBox child = linebox.getBox(k);
1096:
1097: if (child == box) {
1098: // Found it
1099: int lineno = j + 1;
1100:
1101: while (lineno < n) {
1102: CssBox next = parent.getBox(lineno);
1103:
1104: if (next.getBoxType() == BoxType.LINEBOX) {
1105: return (LineBox) next;
1106: }
1107:
1108: lineno++;
1109: }
1110:
1111: break;
1112: }
1113: }
1114: }
1115:
1116: box = parent;
1117: }
1118:
1119: // We're not in a linebox group, or we were already in the first
1120: // line of that line box group...
1121: while (box != null) {
1122: CssBox next = box.getNextNormalBlockBox();
1123:
1124: while (next != null) {
1125: LineBox lb = findFirstLineBox(next);
1126:
1127: if (lb != null) {
1128: return lb;
1129: }
1130:
1131: next = next.getNextNormalBlockBox();
1132: }
1133:
1134: box = box.getParent();
1135: }
1136:
1137: return null;
1138: }
1139:
1140: /**
1141: * Given a box, find the previous linebox in flow searching backwards.
1142: * @param box The box to start the search from (which may be
1143: * inside a linebox. The containing linebox will not be returned
1144: * as the next linebox.
1145: * @return The previous linebox in flow.
1146: */
1147: private static LineBox findPrevLineBox(CssBox box) {
1148: if ((box != null) && (box.getBoxType() != BoxType.LINEBOX)
1149: && box.getParent() instanceof LineBoxGroup) {
1150: // The box is already managed as part of a line box group
1151: // See which line box it's in.
1152: CssBox parent = box.getParent();
1153:
1154: for (int j = 0, n = parent.getBoxCount(); j < n; j++) {
1155: CssBox linebox = parent.getBox(j);
1156:
1157: if (linebox.getBoxType() != BoxType.LINEBOX) {
1158: continue;
1159: }
1160:
1161: for (int k = 0, m = linebox.getBoxCount(); k < m; k++) {
1162: CssBox child = linebox.getBox(k);
1163:
1164: if (child == box) {
1165: // Found it
1166: int lineno = j - 1;
1167:
1168: while (lineno >= 0) {
1169: CssBox prev = parent.getBox(lineno);
1170:
1171: if (prev.getBoxType() == BoxType.LINEBOX) {
1172: return (LineBox) prev;
1173: }
1174:
1175: lineno--;
1176: }
1177:
1178: break;
1179: }
1180: }
1181: }
1182:
1183: box = parent;
1184: }
1185:
1186: while (box != null) {
1187: CssBox prev = box.getPrevNormalBlockBox();
1188:
1189: while (prev != null) {
1190: LineBox lb = findLastLineBox(prev);
1191:
1192: if (lb != null) {
1193: return lb;
1194: }
1195:
1196: prev = prev.getPrevNormalBlockBox();
1197: }
1198:
1199: box = box.getParent();
1200: }
1201:
1202: return null;
1203: }
1204:
1205: /**
1206: * Return the first linebox found in a depth search of this box tree
1207: * @param box The root box to check
1208: * @return The first LineBox found under the box
1209: */
1210: private static LineBox findFirstLineBox(CssBox box) {
1211: if ((box.getBoxType() == BoxType.LINEBOX)
1212: && box instanceof LineBox) { // LineBoxGroup is also BoxType.LINEBOX
1213:
1214: return (LineBox) box;
1215: }
1216:
1217: for (int i = 0, n = box.getBoxCount(); i < n; i++) {
1218: CssBox child = box.getBox(i);
1219:
1220: if (child instanceof ExternalDocumentBox) {
1221: continue;
1222: }
1223:
1224: LineBox match = findFirstLineBox(child);
1225:
1226: if (match != null) {
1227: return match;
1228: }
1229: }
1230:
1231: return null;
1232: }
1233:
1234: /**
1235: * Return the last linebox found in a depth search of this box tree
1236: * @param box The root box to check
1237: * @return The last LineBox found under the box
1238: */
1239: private static LineBox findLastLineBox(CssBox box) {
1240: if ((box.getBoxType() == BoxType.LINEBOX)
1241: && box instanceof LineBox) { // LineBoxGroup is also BoxType.LINEBOX
1242:
1243: return (LineBox) box;
1244: }
1245:
1246: for (int i = box.getBoxCount() - 1; i >= 0; i--) {
1247: CssBox child = box.getBox(i);
1248:
1249: if (child instanceof ExternalDocumentBox) {
1250: continue;
1251: }
1252:
1253: LineBox match = findLastLineBox(child);
1254:
1255: if (match != null) {
1256: return match;
1257: }
1258: }
1259:
1260: return null;
1261: }
1262:
1263: /** Find the LineBox corresponding to the given position */
1264: // private static LineBox findLineBox(PageBox pageBox, Position pos) {
1265: private static LineBox findLineBox(PageBox pageBox, DomPosition pos) {
1266: // CssBox box = findBox(webform.getPane().getPageBox(), pos);
1267: CssBox box = findBox(pageBox, pos);
1268:
1269: if (box == null) {
1270: return null;
1271: }
1272:
1273: return findLineBox(box, pos.getBias() == Bias.FORWARD);
1274: }
1275:
1276: /**
1277: * Find the LineBox corresponding to the given box.
1278: * If the box has an element which spans multiple lines (like a <span> whose
1279: * text doesn't all fit on a single line) the "first" parameter will be
1280: * used to decide if we should return the first linebox matching or the last
1281: * linebox matching this box.
1282: */
1283: private static LineBox findLineBox(CssBox box, boolean first) { // HACK ALERT
1284:
1285: CssBox curr = box;
1286:
1287: while ((curr != null) && /*isSpan() &&*/
1288: !(curr instanceof LineBoxGroup)) {
1289: curr = curr.getParent();
1290: }
1291:
1292: if (curr == null) {
1293: return null;
1294: }
1295:
1296: if (!(curr instanceof LineBoxGroup)) {
1297: // I must have run into an block box or absolutely positioned
1298: // box of some sort;
1299: // Look downwards for the first normal flow child and
1300: // if it's a line box group, use that
1301: CssBox firstBox = curr.getFirstNormalBox();
1302:
1303: if (firstBox instanceof LineBoxGroup) {
1304: // HACK - returning first linebox - should find closest instead!!
1305: // XXX is there any chance this could be a float???
1306: return (LineBox) firstBox.getBox(0);
1307: } else {
1308: return null;
1309: }
1310: }
1311:
1312: LineBoxGroup lbg = (LineBoxGroup) curr;
1313:
1314: for (int i = 0, n = lbg.getBoxCount(); i < n; i++) {
1315: CssBox b = lbg.getBox(i);
1316:
1317: if (b instanceof LineBox) {
1318: LineBox lb = (LineBox) b;
1319:
1320: for (int j = 0, m = lb.getBoxCount(); j < m; j++) {
1321: if (lb.getBox(j) == box) {
1322: return lb;
1323: }
1324: }
1325: } else {
1326: // Floats can appear in a line context.
1327: assert b.getBoxType() == BoxType.FLOAT;
1328:
1329: // Float contents aren't in line boxes
1330: // XXX not true! but won't fix that right now
1331: continue;
1332: }
1333: }
1334:
1335: // We were found in a LineBoxGroup but not in one of its LineBoxes.
1336: // That means we're a managed box that does not render its own inline box,
1337: // such as for example "<b>".
1338: // At this point we could go and search the boxes children to see if any of
1339: // -them- (or their children, e.g. in <b><i><u>Hello...) render to an inline
1340: // box. However the point is we shouldn't have even gotten the caret into
1341: // this position - it should always be associated with an inline position.
1342: // So rather than hack this here (where could be in trouble - what if there
1343: // is no inlinebox inside our cursor position, e.g. <b>^</b> - so nothing
1344: // here is rendered in any linebox, in that case we're hosed. So we need
1345: // to fix the root cause of this.
1346: // Uh oh this is unavoidable.
1347: // Let's say my source has this:
1348: // <h:outputText value="foo"/><h:outputText value="bar"/>
1349: // Clearly I should be able to put the caret in between these texts. But in
1350: // The HTML dom I have this:
1351: // <span>foo</span>^<span>bar</span>
1352: // The only valid caret position here is between the spans (indicated by ^).
1353: // In terms of the sosurce dom this means I can only put the caret in a position
1354: // that is rendered into a managed box - which doesn't appear in the line box
1355: // hierarchy (the <span>.)
1356: // Thus, I need to go here and make sure I can find line boxes for these things
1357: // too. Note that a <span> can span multiple lines. Thus the span may have more
1358: // than one associated linebox.
1359: // Node element = getElement(box);
1360: Node element = getComponentRootElementParentForCssBox(box);
1361: assert element != null;
1362:
1363: if (first) {
1364: // Look for the FIRST box present in a linebox for the given managed box
1365: for (int i = 0, n = lbg.getBoxCount(); i < n; i++) {
1366: CssBox b = lbg.getBox(i);
1367:
1368: if (b instanceof LineBox) {
1369: LineBox lb = (LineBox) b;
1370:
1371: for (int j = 0, m = lb.getBoxCount(); j < m; j++) {
1372: CssBox ilb = lb.getBox(j);
1373: // Node node = getElement(ilb);
1374: Node node = getComponentRootElementParentForCssBox(ilb);
1375:
1376: // Search up the DOM to see if this leaf box is inside the given
1377: // target box' element
1378: while (node != null) {
1379: if (node == element) {
1380: return lb;
1381: }
1382:
1383: node = node.getParentNode();
1384: }
1385: }
1386: } else {
1387: // Floats can appear in a line context.
1388: assert b.getBoxType() == BoxType.FLOAT;
1389:
1390: // Float contents aren't in line boxes
1391: // XXX not true! but won't fix that right now
1392: continue;
1393: }
1394: }
1395: } else {
1396: // Look for the LAST box present in a linebox for the given managed box
1397: // Same as above, but we search backwards in the linebox list as
1398: // well as backwards in each linebox. This ensures that I find the
1399: // last matching box rather than the first.
1400: for (int n = lbg.getBoxCount(), i = n - 1; i >= 0; i--) {
1401: CssBox b = lbg.getBox(i);
1402:
1403: if (b instanceof LineBox) {
1404: LineBox lb = (LineBox) b;
1405:
1406: for (int m = lb.getBoxCount(), j = m - 1; j >= 0; j--) {
1407: CssBox ilb = lb.getBox(j);
1408: // Node node = getElement(ilb);
1409: Node node = getComponentRootElementParentForCssBox(ilb);
1410:
1411: // Search up the DOM to see if this leaf box is inside the given
1412: // target box' element
1413: while (node != null) {
1414: if (node == element) {
1415: return lb;
1416: }
1417:
1418: node = node.getParentNode();
1419: }
1420: }
1421: } else {
1422: // Floats can appear in a line context.
1423: assert b.getBoxType() == BoxType.FLOAT;
1424:
1425: // Float contents aren't in line boxes
1426: // XXX not true! but won't fix that right now
1427: continue;
1428: }
1429: }
1430: }
1431:
1432: return null;
1433: }
1434:
1435: /** Find the box whose corresponding node matches the given node, starting
1436: * at the given root box. The offset is only relevant when we're dealing with
1437: * a text box, where the node is split into many boxes.
1438: */
1439: public static CssBox findBox(CssBox root, Node target, int offset) { // HACK ALERT
1440:
1441: if (root.getBoxType() == BoxType.TEXT) {
1442: TextBox tb = (TextBox) root;
1443:
1444: if ((tb.getNode() == target) && (
1445: /* This doesn't work when the position is somewhere in the DOM I'm
1446: not displaying, like in a region of space characters. Since I search
1447: the dom forwards however, I don't need this extra check.
1448: tb.getDomStartOffset() <= offset && */
1449: offset <= tb.getDomEndOffset())) {
1450: return tb;
1451: }
1452:
1453: return null;
1454: } else if (root.getBoxType() == BoxType.SPACE) {
1455: SpaceBox tb = (SpaceBox) root;
1456:
1457: if ((tb.getNode() == target) && (
1458: /* This doesn't work when the position is somewhere in the DOM I'm
1459: not displaying, like in a region of space characters. Since I search
1460: the dom forwards however, I don't need this extra check.
1461: tb.getDomStartOffset() <= offset && */
1462: offset <= tb.getDomEndOffset())) {
1463: return tb;
1464: }
1465:
1466: return null;
1467: }
1468:
1469: if ((root.getElement() == target)
1470: || (root.getSourceElement() == target)) {
1471: return root;
1472: }
1473:
1474: for (int i = 0, n = root.getBoxCount(); i < n; i++) {
1475: CssBox child = root.getBox(i);
1476: CssBox match = findBox(child, target, offset);
1477:
1478: if (match != null) {
1479: if ((match.getBoxType() == BoxType.SPACE)
1480: && (i < (n - 1))) {
1481: CssBox match2 = findBox(root.getBox(i + 1), target,
1482: offset);
1483:
1484: if (match2 != null) {
1485: if ((match2.getBoxType() == BoxType.TEXT)
1486: && (((TextBox) match2)
1487: .getDomStartOffset() >= offset)) {
1488: return match2;
1489: } else if (match2.getBoxType() != BoxType.SPACE) {
1490: return match2;
1491: }
1492: }
1493: }
1494:
1495: return match;
1496: }
1497: }
1498:
1499: return null;
1500: }
1501:
1502: /** Find the CssBox corresponding to a given position */
1503: // private static CssBox findBox(PageBox root, Position pos) { // HACK ALERT
1504: private static CssBox findBox(PageBox root, DomPosition pos) { // HACK ALERT
1505:
1506: // if (pos == Position.NONE) {
1507: if (pos == DomPosition.NONE) {
1508: return null;
1509: }
1510:
1511: Node node = pos.getNode();
1512: int offset = pos.getOffset();
1513:
1514: if ((node.getNodeType() == Node.TEXT_NODE)
1515: || (node.getNodeType() == Node.CDATA_SECTION_NODE)) {
1516: // Text node: locate the corresponding line box
1517: // The offset may point to a collapsed space character
1518: // so watch out:)
1519: Node p = node.getParentNode();
1520: CssBox box = null;
1521:
1522: // Try to shortcut the search through the box hierarchy for
1523: // the node matching this position by looking up the parent box
1524: if ((p != null) && (p.getNodeType() == Node.ELEMENT_NODE)) {
1525: Element pe = (Element) p;
1526: // CssBox pb = CssBox.getBox(pe);
1527: CssBox pb = root.getWebForm().findCssBoxForElement(pe);
1528:
1529: if (pb != null) {
1530: box = findBox(pb, node, offset);
1531:
1532: if ((box == null)
1533: && (node.getNodeValue().length() == 0)
1534: && (node.getNextSibling() != null)) {
1535: // When I'm doing inline editing, I may be deleting
1536: // text within a node, so I end up pointing to
1537: // an empty text. If so try to look for the position
1538: // next to it instead.
1539: box = findBox(pb, node.getNextSibling(), 0);
1540: }
1541:
1542: if ((box == null)
1543: && DesignerUtils.onlyWhitespace(node
1544: .getNodeValue())) {
1545: // Perhaps it's a space box we've pulled
1546: if (pos.getBias() == Bias.FORWARD) {
1547: if (node.getNextSibling() != null) {
1548: box = findBox(pb,
1549: node.getNextSibling(), 0);
1550: }
1551: } else { // Bias.BACKWARD
1552:
1553: if (node.getPreviousSibling() != null) {
1554: box = findBox(pb, node
1555: .getPreviousSibling(), 0);
1556: }
1557: }
1558: }
1559: }
1560: }
1561:
1562: if (box == null) {
1563: box = findBox(root, node, offset);
1564: }
1565:
1566: return box;
1567: } else if (node.getNodeType() == Node.ELEMENT_NODE) {
1568: // Element node
1569: // offset 0 means before the element, offset 1 means after
1570: Element element = (Element) node;
1571: NodeList list = element.getChildNodes();
1572: int len = list.getLength();
1573:
1574: if ((len == 0) || (offset >= len)) {
1575: // We have a 0 index but no children, e.g. we're
1576: // inside an empty element.
1577: // XXX Can we do anything here? For now just use the parent
1578: // element
1579: } else if ((pos.getBias() == Bias.BACKWARD) && (offset > 0)
1580: && ((offset - 1) < len)) {
1581: Node child = list.item(offset - 1);
1582:
1583: if (child.getNodeType() == Node.ELEMENT_NODE) {
1584: element = (Element) child;
1585: } else if ((child.getNodeType() == Node.TEXT_NODE)
1586: || (child.getNodeType() == Node.CDATA_SECTION_NODE)) {
1587: // CssBox pb = CssBox.getBox(element);
1588: CssBox pb = root.getWebForm().findCssBoxForElement(
1589: element);
1590:
1591: CssBox box = null;
1592:
1593: if (pb != null) {
1594: box = findBox(pb, child, offset);
1595:
1596: if ((box == null)
1597: && (child.getNodeValue().length() == 0)
1598: && (child.getNextSibling() != null)) {
1599: // When I'm doing inline editing, I may be deleting
1600: // text within a node, so I end up pointing to
1601: // an empty text. If so try to look for the position
1602: // next to it instead.
1603: box = findBox(pb, child.getNextSibling(), 0);
1604: }
1605: }
1606:
1607: if (box == null) {
1608: box = findBox(root, child, offset);
1609: }
1610:
1611: return box;
1612: }
1613: } else if (offset < len) {
1614: Node child = list.item(offset);
1615:
1616: if (child.getNodeType() == Node.ELEMENT_NODE) {
1617: element = (Element) child;
1618: } // else use parent -- which we already have stored in element
1619: }
1620:
1621: // CssBox box = CssBox.getBox(element);
1622: CssBox box = root.getWebForm()
1623: .findCssBoxForElement(element);
1624:
1625: // Todo: find the nearest linebox
1626: return box;
1627: } else {
1628: // Not sure how to handle this one
1629: ErrorManager.getDefault().log(
1630: "Unexpected node type in findBox: " + node);
1631:
1632: return null;
1633: }
1634: }
1635:
1636: /**
1637: * For the given caret position, compute the visible caret rectangle
1638: * on the screen. Note that the position must fit certain criteria:
1639: * it needs to point inside a LineBox (e.g. be a valid caret position).
1640: * @param A position in the source dom (where carets live for example)
1641: */
1642: // public static Rectangle modelToView(PageBox pageBox, Position sourcePos) {
1643: public static Rectangle modelToView(PageBox pageBox,
1644: DomPosition sourcePos) {
1645: if (pageBox == null) {
1646: // XXX #122515 NPE, which shouldn't happen (it seems there is a deeper issue).
1647: return null;
1648: }
1649: // assert that the position is a valid position here?
1650: // if (sourcePos == Position.NONE) {
1651: if (sourcePos == DomPosition.NONE) {
1652: return null;
1653: }
1654:
1655: // assert !sourcePos.isRendered();
1656: // if (MarkupService.isRenderedNode(sourcePos.getNode())) {
1657: if (sourcePos.isRenderedPosition()) {
1658: ErrorManager.getDefault().notify(
1659: ErrorManager.INFORMATIONAL,
1660: new IllegalArgumentException(
1661: "Node is expected to be not rendered, node="
1662: + sourcePos.getNode())); // NOI18N
1663: }
1664:
1665: // Position pos = sourcePos.getRenderedPosition();
1666: DomPosition pos = sourcePos.getRenderedPosition();
1667:
1668: // if (pos == Position.NONE) {
1669: if (pos == DomPosition.NONE) {
1670: return null; // not yet rendered
1671: }
1672:
1673: Node node = pos.getNode();
1674: int offset = pos.getOffset();
1675:
1676: if (node.getNodeType() == Node.ELEMENT_NODE) {
1677: // XXX this should not be necessary!
1678: //XhtmlElement xel = (XhtmlElement)node;
1679: //node = xel.getSourceElement();
1680: if (node.getChildNodes().getLength() == 0) {
1681: // Have no children
1682: // Try to use font height instead
1683: Element element = (Element) node;
1684: // CssBox box = CssBox.getBox(element);
1685: CssBox box = pageBox.getWebForm().findCssBoxForElement(
1686: element);
1687:
1688: // Find the first actual item in a linebox since parents
1689: // in inline context don't get inserted. But what about floats??
1690: while ((box != null)
1691: && (box.getHeight() == CssBox.UNINITIALIZED)
1692: && (box.getBoxCount() > 0)) {
1693: box = box.getBox(0);
1694: }
1695:
1696: // Todo: find the nearest linebox
1697: if (box != null) {
1698: int height = box.getHeight();
1699: // Font font = CssLookup.getFont(element, DesignerSettings.getInstance().getDefaultFontSize());
1700: // Font font = CssProvider.getValueService().getFontForElement(element, DesignerSettings.getInstance().getDefaultFontSize(), Font.PLAIN);
1701: // if (font != null) {
1702: // FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
1703: // height = metrics.getHeight();
1704: // }
1705: // XXX Missing text.
1706: height = CssUtilities
1707: .getDesignerFontMetricsForElement(
1708: element,
1709: null,
1710: pageBox.getWebForm()
1711: .getDefaultFontSize())
1712: .getHeight();
1713:
1714: Rectangle r = new Rectangle(box.getAbsoluteX(), box
1715: .getAbsoluteY(), box.getWidth(), height);
1716:
1717: if (box.isInlineBox()) {
1718: r.y = (box.getAbsoluteY() + box.getHeight())
1719: - height;
1720: }
1721:
1722: return r;
1723: }
1724:
1725: return null;
1726: } else if ((pos.getBias() == Bias.BACKWARD)
1727: && (offset > 0)
1728: && ((offset - 1) < node.getChildNodes().getLength())) {
1729: node = node.getChildNodes().item(offset - 1);
1730: offset = 1;
1731: } else if (offset < node.getChildNodes().getLength()) {
1732: node = node.getChildNodes().item(offset);
1733: offset = 0;
1734: } else if (offset > 0) {
1735: // XXX not a good idea for <br> !
1736: // XXX This is highly suspect!!! I should probably use node.getChildNodes().getLength()-1 here!!! not offset-1
1737: node = node.getChildNodes().item(offset - 1);
1738:
1739: if (node.getNodeType() == Node.TEXT_NODE) {
1740: offset = node.getNodeValue().length();
1741: } else {
1742: offset = 1; // XXX ?
1743: }
1744: }
1745: } else if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
1746: if (node.getChildNodes().getLength() == 0) {
1747: // Have no children
1748: return null; // this shouldn't happen!!
1749: } else if ((pos.getBias() == Bias.BACKWARD)
1750: && (offset > 0)
1751: && ((offset - 1) < node.getChildNodes().getLength())) {
1752: node = node.getChildNodes().item(offset - 1);
1753: offset = 1;
1754: } else if (offset < node.getChildNodes().getLength()) {
1755: node = node.getChildNodes().item(offset);
1756: offset = 0;
1757: } else if (offset > 0) {
1758: // XXX not a good idea for <br> !
1759: node = node.getChildNodes().item(offset - 1);
1760:
1761: if (node.getNodeType() == Node.TEXT_NODE) {
1762: offset = node.getNodeValue().length();
1763: } else if (node.getNodeType() == Node.ELEMENT_NODE) {
1764: return modelToView(pageBox,
1765: // new Position(node, node.getChildNodes().getLength(), Bias.FORWARD));
1766: // Position.create(node, node.getChildNodes().getLength(), Bias.FORWARD));
1767: pageBox.getWebForm().createDomPosition(
1768: node,
1769: node.getChildNodes().getLength(),
1770: Bias.FORWARD));
1771: } else {
1772: offset = 1; // XXX ?
1773: }
1774: }
1775: }
1776:
1777: if ((node.getNodeType() == Node.TEXT_NODE)
1778: || (node.getNodeType() == Node.CDATA_SECTION_NODE)) {
1779: // // XXX this should not be necessary!
1780: // if (node instanceof XhtmlText) {
1781: // XhtmlText xt = (XhtmlText)node;
1782: // node = xt.getSourceNode();
1783: // }
1784: // Text node: locate the corresponding line box
1785: // The offset may point to a collapsed space character
1786: // so watch out:)
1787: Node p = node.getParentNode();
1788: CssBox pb = null;
1789: CssBox box = null;
1790:
1791: // Try to shortcut the search through the box hierarchy for
1792: // the node matching this position by looking up the parent box
1793: if ((p != null) && (p.getNodeType() == Node.ELEMENT_NODE)) {
1794: Element pe = (Element) p;
1795: // pb = CssBox.getBox(pe);
1796: pb = pageBox.getWebForm().findCssBoxForElement(pe);
1797:
1798: if (pb != null) {
1799: // XXX here we should use the SOURCE node!
1800: box = findBox(pb, node, offset);
1801: }
1802:
1803: // if (box == null) {
1804: // LineBox lb = findLineBox(pb);
1805: // if (lb != null) {
1806: // box = findBox(lb, node, offset);
1807: // }
1808: // }
1809: }
1810:
1811: if (box == null) {
1812: box = findBox(pageBox, node, offset);
1813: }
1814:
1815: if ((box == null)
1816: && ((node.getNodeType() == Node.TEXT_NODE) || (node
1817: .getNodeType() == Node.CDATA_SECTION_NODE))
1818: && (node.getNextSibling() != null)
1819: && (node.getNextSibling().getNodeType() == Node.ELEMENT_NODE)
1820: && ((Element) node.getNextSibling()).getTagName()
1821: .equals(HtmlTag.BR.name)) {
1822: box = findBox(pageBox, node.getNextSibling(), 0);
1823: }
1824:
1825: if (box != null) {
1826: int x = box.getAbsoluteX();
1827:
1828: if (box.getBoxType() == BoxType.TEXT) {
1829: // return ((TextBox)box).getBoundingBox(new Position(node, offset, Bias.FORWARD));
1830: // return ((TextBox)box).getBoundingBox(Position.create(node, offset, Bias.FORWARD));
1831: return ((TextBox) box).getBoundingBox(pageBox
1832: .getWebForm().createDomPosition(node,
1833: offset, Bias.FORWARD));
1834: } else if (box.getBoxType() == BoxType.SPACE) {
1835: return ((SpaceBox) box).getBoundingBox(pageBox
1836: .getWebForm().createDomPosition(node,
1837: offset, Bias.FORWARD));
1838:
1839: /*
1840: } else if (box instanceof FormComponentBox) {
1841: return ((FormComponentBox)box).getBoundingBox(new Position(node, offset));
1842: */
1843: }
1844:
1845: return new Rectangle(x, box.getAbsoluteY(), box
1846: .getWidth(), box.getHeight());
1847: }
1848:
1849: if ((offset > 0)
1850: && (node.getNodeValue().charAt(offset - 1) == ' ')) {
1851: // Special case: the caret is after a space at the end of a text node;
1852: // in this case, since we don't create boxes for spaces, there's no
1853: // corresponding box recording the location. So find the most recent
1854: // box instead.
1855: box = findBox(pageBox, node, offset - 1);
1856:
1857: if (box != null) {
1858: Rectangle r = null;
1859:
1860: if (box.getBoxType() == BoxType.TEXT) {
1861: // r = ((TextBox)box).getBoundingBox(new Position(node, offset, Bias.FORWARD));
1862: // r = ((TextBox)box).getBoundingBox(Position.create(node, offset, Bias.FORWARD));
1863: r = ((TextBox) box).getBoundingBox(pageBox
1864: .getWebForm().createDomPosition(node,
1865: offset, Bias.FORWARD));
1866: r.width += ((TextBox) box).getMetrics()
1867: .charWidth(' ');
1868:
1869: return r;
1870: } else if (box.getBoxType() == BoxType.SPACE) {
1871: // r = ((SpaceBox)box).getBoundingBox(new Position(node, offset, Bias.FORWARD));
1872: // r = ((SpaceBox)box).getBoundingBox(Position.create(node, offset, Bias.FORWARD));
1873: r = ((SpaceBox) box).getBoundingBox(pageBox
1874: .getWebForm().createDomPosition(node,
1875: offset, Bias.FORWARD));
1876: r.width += ((SpaceBox) box).getMetrics()
1877: .charWidth(' ');
1878:
1879: return r;
1880: }
1881:
1882: int SPACE = 5; // hack - gotta find true space width
1883:
1884: return new Rectangle(box.getAbsoluteX() + SPACE,
1885: box.getAbsoluteY(), box.getWidth(), box
1886: .getHeight());
1887: }
1888: }
1889:
1890: // XXX do I have the same issue with space in front of
1891: // boxes too?
1892: if (pb != null) {
1893: int height = pb.getHeight();
1894: // Font font = CssLookup.getFont(pb.getElement(), DesignerSettings.getInstance().getDefaultFontSize());
1895: // Font font = CssProvider.getValueService().getFontForElement(pb.getElement(), DesignerSettings.getInstance().getDefaultFontSize(), Font.PLAIN);
1896: // if (font != null) {
1897: // FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
1898: // height = metrics.getHeight();
1899: // }
1900: // XXX Missing text.
1901: height = CssUtilities.getDesignerFontMetricsForElement(
1902: pb.getElement(), null,
1903: pageBox.getWebForm().getDefaultFontSize())
1904: .getHeight();
1905:
1906: // Try to find a sibling box
1907: Node curr = node.getPreviousSibling();
1908:
1909: while (curr != null) {
1910: box = findBox(pb, curr, 0);
1911:
1912: if (box != null) {
1913: break;
1914: }
1915:
1916: curr = curr.getPreviousSibling();
1917: }
1918:
1919: if (box != null) {
1920: // THIS IS A HACK! Just trying to get closer to the real position of the cursor
1921: Rectangle r = new Rectangle(box.getAbsoluteX()
1922: + box.getWidth(), box.getAbsoluteY(), box
1923: .getWidth(), height);
1924:
1925: // XXX adjust to get to the bottom as well??? (if inline box) as done elsewhere...
1926: curr = curr.getNextSibling();
1927:
1928: while (curr != node) {
1929: if (curr instanceof Element
1930: && ((Element) curr).getTagName()
1931: .equals("br")) {
1932: // newline
1933: r.y += height;
1934: r.x = box.getAbsoluteX();
1935: }
1936:
1937: curr = curr.getNextSibling();
1938: }
1939:
1940: return r;
1941: }
1942:
1943: Rectangle r = new Rectangle(pb.getAbsoluteX(), pb
1944: .getAbsoluteY(), pb.getWidth(), height);
1945:
1946: return r;
1947: }
1948:
1949: return null;
1950: } else if (node.getNodeType() == Node.ELEMENT_NODE) {
1951: // Element node
1952: // offset 0 means before the element, offset 1 means after
1953: Element element = (Element) node;
1954: // CssBox box = CssBox.getBox(element);
1955: CssBox box = pageBox.getWebForm().findCssBoxForElement(
1956: element);
1957:
1958: // Find the first actual item in a linebox since parents
1959: // in inline context don't get inserted. But what about floats??
1960: while ((box != null)
1961: && (box.getHeight() == CssBox.UNINITIALIZED)
1962: && (box.getBoxCount() > 0)) {
1963: box = box.getBox(0);
1964: }
1965:
1966: if (box == null) {
1967: box = findBox(pageBox, node, offset);
1968: }
1969:
1970: // if ((box != null) && (box.getDesignBean() == null)) {
1971: // if ((box != null) && (CssBox.getMarkupDesignBeanForCssBox(box) == null)) {
1972: if ((box != null)
1973: && (CssBox.getElementForComponentRootCssBox(box) == null)) {
1974: // We have a generated box... go find the associated line box
1975: LineBox lb = findLineBox(box, offset == 0);
1976:
1977: if (lb != null) {
1978: //CssBox lastBox = findBeanBox(lb, box.getDesignBean(), offset == 0);
1979: CssBox lastBox = null;
1980: // DesignBean bean = getBean(box);
1981: Element parentComponentRootElement = getComponentRootElementParentForCssBox(box);
1982: boolean first = (offset == 0);
1983:
1984: CssBox beanBox = null;
1985:
1986: for (int i = 0, n = lb.getBoxCount(); i < n; i++) {
1987: CssBox ilb = lb.getBox(i);
1988:
1989: // if (getBean(ilb) == bean) {
1990: if (getComponentRootElementParentForCssBox(ilb) == parentComponentRootElement) {
1991: beanBox = ilb;
1992:
1993: if (first) {
1994: break;
1995: }
1996: }
1997: }
1998:
1999: lastBox = beanBox;
2000:
2001: if (lastBox != null) {
2002: box = lastBox;
2003: }
2004: }
2005: }
2006:
2007: // Todo: find the nearest linebox
2008: if (box != null) {
2009: int height = box.getHeight();
2010:
2011: // Try to use font height instead
2012: // Font font = CssLookup.getFont(element, DesignerSettings.getInstance().getDefaultSize());
2013: // Font font = CssProvider.getValueService().getFontForElement(element, DesignerSettings.getInstance().getDefaultFontSize(), Font.PLAIN);
2014: // if (font != null) {
2015: // FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
2016: // height = metrics.getHeight();
2017: // }
2018: // XXX Missing text.
2019: height = CssUtilities.getDesignerFontMetricsForElement(
2020: element, null,
2021: pageBox.getWebForm().getDefaultFontSize())
2022: .getHeight();
2023:
2024: Rectangle r = new Rectangle(box.getAbsoluteX(), box
2025: .getAbsoluteY(), box.getWidth(), height);
2026:
2027: if (offset == 1) {
2028: r.x += box.getWidth();
2029: }
2030:
2031: if (box.isInlineBox()) {
2032: r.y = (box.getAbsoluteY() + box.getHeight())
2033: - height;
2034: }
2035:
2036: return r;
2037: }
2038:
2039: return null;
2040: } else {
2041: // Not sure how to handle this one
2042: ErrorManager.getDefault().log(
2043: "Unexpected node type in modelToView: " + node);
2044:
2045: return null;
2046: }
2047: }
2048:
2049: // /** Find the DesignBean associated with a particular box. */
2050: // private static DesignBean getBean(CssBox box) {
2051: //// RaveElement element = getElement(box);
2052: // Element element = getElement(box);
2053: //
2054: //// if (element != null) {
2055: //// return element.getDesignBean();
2056: //// }
2057: ////
2058: //// return null;
2059: //// return InSyncService.getProvider().getMarkupDesignBeanForElement(element);
2060: // return WebForm.getDomProviderService().getMarkupDesignBeanForElement(element);
2061: // }
2062: //
2063: // private static Element getElement(CssBox box) {
2064: //// RaveElement element = (RaveElement)box.getElement();
2065: // Element element = box.getElement();
2066: //
2067: // while (true) {
2068: // //if (element.getDesignBean() != null) {
2069: // // return element;
2070: // //}
2071: //// if (element.getSource() != null) {
2072: // if (MarkupService.getSourceElementForElement(element) != null) {
2073: // return element;
2074: // }
2075: //
2076: // Node parent = element.getParentNode();
2077: //
2078: // if (parent instanceof Element) {
2079: // element = (Element)parent;
2080: // } else {
2081: // return null;
2082: // }
2083: // }
2084: // }
2085:
2086: private static Element getComponentRootElementParentForCssBox(
2087: CssBox cssBox) {
2088: while (true) {
2089: Element componentRootElement = CssBox
2090: .getElementForComponentRootCssBox(cssBox);
2091: if (componentRootElement != null) {
2092: return componentRootElement;
2093: }
2094:
2095: cssBox = cssBox.getParent();
2096: if (cssBox == null) {
2097: return null;
2098: }
2099: }
2100: }
2101:
2102: /**
2103: * Provides a mapping from the view coordinate space to the logical
2104: * coordinate space of the model.
2105: * The returned position is always a position in the source DOM,
2106: * not a position in the rendered DOM (unless it is Position.NONE
2107: * which means the position does not correlate to a position in the document.)
2108: *
2109: *
2110: * @todo Find a better home for this method
2111: * @param x the X coordinate >= 0
2112: * @param y the Y coordinate >= 0
2113: * @return the location within the model that best represents the
2114: * given point in the view >= 0.
2115: */
2116: // public static Position viewToModel(WebForm webform, int x, int y) {
2117: public static DomPosition viewToModel(WebForm webform, int x, int y) {
2118: try {
2119: // Position pos = findClosestPosition(webform, x, y);
2120: DomPosition pos = findClosestPosition(webform, x, y);
2121:
2122: // if ((pos != Position.NONE) && pos.isRendered()) {
2123: // if ((pos != Position.NONE)
2124: if ((pos != DomPosition.NONE)
2125: // && MarkupService.isRenderedNode(pos.getNode())) {
2126: && pos.isRenderedPosition()) {
2127: return pos.getSourcePosition();
2128: } else {
2129: return pos;
2130: }
2131:
2132: // Assert that the position computed is a valid caret position (e.g.
2133: // points inside a LineBox) here?
2134: } catch (Throwable t) {
2135: ErrorManager.getDefault().notify(t);
2136:
2137: // return Position.NONE;
2138: return DomPosition.NONE;
2139: }
2140: }
2141:
2142: /** For the given box, locate the position closest to the given x position
2143: */
2144: // private static Position findClosestPosition(WebForm webform, int x, int y) {
2145: private static DomPosition findClosestPosition(WebForm webform,
2146: int x, int y) {
2147: CssBox box = webform.getPane().getPageBox().findCssBox((int) x,
2148: (int) y);
2149:
2150: if (box == null) {
2151: // return Position.NONE;
2152: return DomPosition.NONE;
2153: }
2154:
2155: assert !box.isPlaceHolder();
2156:
2157: if (box instanceof LineBoxGroup) {
2158: // We have clicked on a line that is not covered by a LineBox but
2159: // is inside a group of line boxes - e.g. this line is shorter than
2160: // others. Find it.
2161: // Position pos = findLineBoxClosestTo(box, y, webform.getManager().getInlineEditor());
2162: DomPosition pos = findLineBoxClosestTo(box, y, webform
2163: .getManager().getInlineEditor());
2164:
2165: // if (pos != Position.NONE) {
2166: if (pos != DomPosition.NONE) {
2167: return pos;
2168: }
2169: }
2170:
2171: // If you hit a linebox, just use that
2172: LineBox lb = findLineBox(box, true);
2173:
2174: if (lb != null) {
2175: // Position p = lb.computePosition(x - lb.getAbsoluteX());
2176: DomPosition p = lb.computePosition(x - lb.getAbsoluteX());
2177:
2178: // if (DesignerUtils.checkPosition(p, false, webform.getManager().getInlineEditor()) != Position.NONE) {
2179: // if (isValidPosition(p, false, webform.getManager().getInlineEditor())) {
2180: if (isValidPosition(webform, p, false, webform.getManager()
2181: .getInlineEditor())) {
2182: return p;
2183: }
2184: }
2185:
2186: // Position pos = findLineBoxClosestTo(box, y, webform.getManager().getInlineEditor());
2187: DomPosition pos = findLineBoxClosestTo(box, y, webform
2188: .getManager().getInlineEditor());
2189:
2190: // if (pos != Position.NONE) {
2191: if (pos != DomPosition.NONE) {
2192: return pos;
2193: }
2194:
2195: // No linebox, so look both above and below for matches,
2196: // and pick the closest location
2197: LineBox prevLine = findPrevLineBox(box);
2198:
2199: if (prevLine == null) {
2200: prevLine = findLastLineBox(box);
2201: }
2202:
2203: Rectangle prev = null;
2204: // Position prevPos = null;
2205: DomPosition prevPos = null;
2206:
2207: if (prevLine != null) {
2208: prevPos = findLastLineboxPosition(prevLine, webform
2209: .getManager().getInlineEditor());
2210:
2211: // if (prevPos != Position.NONE) {
2212: if (prevPos != DomPosition.NONE) {
2213: // if (prevPos.isRendered()) {
2214: // if (MarkupService.isRenderedNode(prevPos.getNode())) {
2215: if (prevPos.isRenderedPosition()) {
2216: prevPos = prevPos.getSourcePosition();
2217: }
2218:
2219: prev = modelToView(webform.getPane().getPageBox(),
2220: prevPos);
2221: }
2222: }
2223:
2224: LineBox nextLine = findNextLineBox(box);
2225:
2226: if (nextLine == null) {
2227: nextLine = findFirstLineBox(box);
2228: }
2229:
2230: Rectangle next = null;
2231: // Position nextPos = null;
2232: DomPosition nextPos = null;
2233:
2234: if (nextLine != null) {
2235: nextPos = findFirstLineboxPosition(nextLine, 0, webform
2236: .getManager().getInlineEditor());
2237:
2238: // if (nextPos != Position.NONE) {
2239: if (nextPos != DomPosition.NONE) {
2240: // if (nextPos.isRendered()) {
2241: // if (MarkupService.isRenderedNode(nextPos.getNode())) {
2242: if (nextPos.isRenderedPosition()) {
2243: nextPos = nextPos.getSourcePosition();
2244: }
2245:
2246: next = modelToView(webform.getPane().getPageBox(),
2247: nextPos);
2248: }
2249: }
2250:
2251: if ((next != null) && (prev != null)) {
2252: if (prevLine == nextLine) {
2253: return prevPos;
2254: }
2255:
2256: // See if we're closer to the first or the last line box - pick whichever
2257: // one is closest. Note however that we only look at the y coordinate, not
2258: // the real distance (Math.sqrt(dx*dx+dy*dy))
2259: if (Math.abs(y - next.y) < Math.abs(y - prev.y)) {
2260: return nextPos;
2261: } else {
2262: return prevPos;
2263: }
2264: } else if (next != null) {
2265: return nextPos;
2266: } else if (prev != null) {
2267: return prevPos;
2268: } else {
2269: /* XXX I can't do this because I'm called when the read lock for
2270: * view hierarchy is held, and calling into DesignBean creation
2271: * will require a write lock on the unit...
2272: if (!webform.isGridMode()) {
2273: // There are no caret positions in the document. We should create one.
2274: // Are we closer to the top or the end? Just use y position.
2275: if (y > pageBox.getHeight()/2) {
2276: return createEndPosition();
2277: } else {
2278: return createBeginPosition();
2279: }
2280: }
2281: */
2282:
2283: //assert prevPos == Position.NONE && nextPos == Position.NONE : prevPos + "," + nextPos;
2284: // return Position.NONE;
2285: return DomPosition.NONE;
2286: }
2287: }
2288:
2289: // private static Position findLineBoxClosestTo(CssBox box, int y, InlineEditor inlineEditor) {
2290: private static DomPosition findLineBoxClosestTo(CssBox box, int y,
2291: InlineEditor inlineEditor) {
2292: if ((box.getBoxType() == BoxType.LINEBOX)
2293: && box instanceof LineBox) {
2294: LineBox lb = (LineBox) box;
2295: int cy = lb.getAbsoluteY();
2296:
2297: if ((y == CssBox.AUTO)
2298: || ((y >= cy) && (y <= (cy + lb.getHeight())))) {
2299: return findLastLineboxPosition(lb, inlineEditor);
2300: }
2301: }
2302:
2303: if (!(box instanceof ExternalDocumentBox)) {
2304: // We've clicked outside of any of the children; see
2305: // if we can find a closer one
2306: for (int i = 0, n = box.getBoxCount(); i < n; i++) {
2307: // Position match = findLineBoxClosestTo(box.getBox(i), y, inlineEditor);
2308: DomPosition match = findLineBoxClosestTo(box.getBox(i),
2309: y, inlineEditor);
2310:
2311: if (match != null) {
2312: return match;
2313: }
2314: }
2315: }
2316:
2317: // return Position.NONE;
2318: return DomPosition.NONE;
2319: }
2320:
2321: /** Return the position at the beginning of the line containing the given position */
2322: // public static Position getLineBegin(WebForm webform, Position sourcePos) {
2323: public static DomPosition getLineBegin(WebForm webform,
2324: DomPosition sourcePos) {
2325: // assert !sourcePos.isRendered();
2326: // if (MarkupService.isRenderedNode(sourcePos.getNode())) {
2327: if (sourcePos.isRenderedPosition()) {
2328: ErrorManager.getDefault().notify(
2329: ErrorManager.INFORMATIONAL,
2330: new IllegalArgumentException(
2331: "Node is expected to be not rendered, node="
2332: + sourcePos.getNode())); // NOI18N
2333: }
2334:
2335: // Position pos = sourcePos.getRenderedPosition();
2336: DomPosition pos = sourcePos.getRenderedPosition();
2337:
2338: LineBox lb = findLineBox(webform.getPane().getPageBox(), pos);
2339:
2340: if (lb == null) {
2341: assert false : pos; // Caret positions should always be in a linebox!!
2342:
2343: // return Position.NONE;
2344: return DomPosition.NONE;
2345: }
2346:
2347: // Position p = findFirstLineboxPosition(lb, 0, webform.getManager().getInlineEditor());
2348: DomPosition p = findFirstLineboxPosition(lb, 0, webform
2349: .getManager().getInlineEditor());
2350:
2351: return p.getSourcePosition();
2352: }
2353:
2354: /** Return the position at the end of the line containing the given position */
2355: // public static Position getLineEnd(WebForm webform, Position sourcePos) {
2356: public static DomPosition getLineEnd(WebForm webform,
2357: DomPosition sourcePos) {
2358: // assert !sourcePos.isRendered();
2359: // if (MarkupService.isRenderedNode(sourcePos.getNode())) {
2360: if (sourcePos.isRenderedPosition()) {
2361: ErrorManager.getDefault().notify(
2362: ErrorManager.INFORMATIONAL,
2363: new IllegalArgumentException(
2364: "Node is expected to be not rendered, node="
2365: + sourcePos.getNode())); // NOI18N
2366: }
2367:
2368: // Position pos = sourcePos.getRenderedPosition();
2369: DomPosition pos = sourcePos.getRenderedPosition();
2370:
2371: LineBox lb = findLineBox(webform.getPane().getPageBox(), pos);
2372:
2373: if (lb == null) {
2374: // assert false : pos; // Caret positions should always be in a linebox!!
2375:
2376: // return Position.NONE;
2377: return DomPosition.NONE;
2378: }
2379:
2380: // Position p = findLastLineboxPosition(lb, webform.getManager().getInlineEditor());
2381: DomPosition p = findLastLineboxPosition(lb, webform
2382: .getManager().getInlineEditor());
2383:
2384: return p.getSourcePosition();
2385: }
2386:
2387: /** Find the beginning of the word from the given position */
2388: // public static Position getWordStart(PageBox pageBox, Position sourcePos) {
2389: public static DomPosition getWordStart(PageBox pageBox,
2390: DomPosition sourcePos) {
2391: // assert !sourcePos.isRendered();
2392: // if (MarkupService.isRenderedNode(sourcePos.getNode())) {
2393: if (sourcePos.isRenderedPosition()) {
2394: ErrorManager.getDefault().notify(
2395: ErrorManager.INFORMATIONAL,
2396: new IllegalArgumentException(
2397: "Node is expected to be not rendered, node="
2398: + sourcePos.getNode()));
2399: }
2400:
2401: // Position pos = sourcePos.getRenderedPosition();
2402: DomPosition pos = sourcePos.getRenderedPosition();
2403:
2404: // PageBox pageBox = webform.getPane().getPageBox();
2405: CssBox box = findBox(pageBox, pos);
2406:
2407: // XXX #110083 Possible NPE.
2408: if (box == null) {
2409: return DomPosition.NONE;
2410: }
2411:
2412: if (box.getBoxType() == BoxType.TEXT) {
2413: TextBox tb = (TextBox) box;
2414: // Position p = new Position(tb.getNode(), tb.getDomStartOffset(), Bias.FORWARD);
2415: // Position p = Position.create(tb.getNode(), tb.getDomStartOffset(), Bias.FORWARD);
2416: DomPosition p = pageBox.getWebForm().createDomPosition(
2417: tb.getNode(), tb.getDomStartOffset(), Bias.FORWARD);
2418:
2419: return p.getSourcePosition();
2420: } else if (box.getBoxType() == BoxType.SPACE) {
2421: SpaceBox tb = (SpaceBox) box;
2422: // Position p = new Position(tb.getNode(), tb.getDomStartOffset(), Bias.FORWARD);
2423: // Position p = Position.create(tb.getNode(), tb.getDomStartOffset(), Bias.FORWARD);
2424: DomPosition p = pageBox.getWebForm().createDomPosition(
2425: tb.getNode(), tb.getDomStartOffset(), Bias.FORWARD);
2426:
2427: return p.getSourcePosition();
2428: } else {
2429: // return Position.NONE;
2430: return DomPosition.NONE;
2431: }
2432: }
2433:
2434: /** Find the end of the word from the given position */
2435: // public static Position getWordEnd(PageBox pageBox, Position sourcePos) {
2436: public static DomPosition getWordEnd(PageBox pageBox,
2437: DomPosition sourcePos) {
2438: // assert !sourcePos.isRendered();
2439: // if (MarkupService.isRenderedNode(sourcePos.getNode())) {
2440: if (sourcePos.isRenderedPosition()) {
2441: ErrorManager.getDefault().notify(
2442: ErrorManager.INFORMATIONAL,
2443: new IllegalArgumentException(
2444: "Node is expected to be not rendered, node="
2445: + sourcePos.getNode()));
2446: }
2447:
2448: // Position pos = sourcePos.getRenderedPosition();
2449: DomPosition pos = sourcePos.getRenderedPosition();
2450:
2451: // PageBox pageBox = webform.getPane().getPageBox();
2452: CssBox box = findBox(pageBox, pos);
2453:
2454: // XXX #127014 Possible NPE.
2455: if (box == null) {
2456: return DomPosition.NONE;
2457: }
2458:
2459: if (box.getBoxType() == BoxType.TEXT) {
2460: TextBox tb = (TextBox) box;
2461: // Position p = new Position(tb.getNode(), tb.getDomEndOffset(), Bias.BACKWARD);
2462: // Position p = Position.create(tb.getNode(), tb.getDomEndOffset(), Bias.BACKWARD);
2463: DomPosition p = pageBox.getWebForm().createDomPosition(
2464: tb.getNode(), tb.getDomEndOffset(), Bias.BACKWARD);
2465:
2466: return p.getSourcePosition();
2467: } else if (box.getBoxType() == BoxType.SPACE) {
2468: SpaceBox tb = (SpaceBox) box;
2469: // Position p = new Position(tb.getNode(), tb.getDomEndOffset(), Bias.BACKWARD);
2470: // Position p = Position.create(tb.getNode(), tb.getDomEndOffset(), Bias.BACKWARD);
2471: DomPosition p = pageBox.getWebForm().createDomPosition(
2472: tb.getNode(), tb.getDomEndOffset(), Bias.BACKWARD);
2473:
2474: return p.getSourcePosition();
2475: } else {
2476: // return Position.NONE;
2477: return DomPosition.NONE;
2478: }
2479: }
2480:
2481: /**
2482: * Return the first caret position in the document. If create is true,
2483: * create one if necessary.
2484: */
2485: // public static Position getFirstDocumentPosition(WebForm webform, boolean create) {
2486: public static DomPosition getFirstDocumentPosition(WebForm webform,
2487: boolean create) {
2488: // XXX #128116 Possible NPE after closing component.
2489: if (webform.getPane().getPageBox() == null) {
2490: return DomPosition.NONE;
2491: }
2492:
2493: // XXX Very suspicious assertion.
2494: // assert webform.getPane().getPageBox().getElement().getOwnerDocument() == webform.getJspDom();
2495: if (webform.getPane().getPageBox().getElement()
2496: .getOwnerDocument() != webform.getHtmlDom()) {
2497: ErrorManager.getDefault().notify(
2498: ErrorManager.INFORMATIONAL,
2499: new IllegalStateException(
2500: "Owner document is expected to be html dom="
2501: + webform.getHtmlDom() // NOI18N
2502: + ", but it is dom="
2503: + webform.getPane().getPageBox()
2504: .getElement()
2505: .getOwnerDocument())); // NOI18N
2506: }
2507:
2508: // First try to put the caret inside the the default parent of the component.
2509: // This will avoid bugs like 6252951 where we start out with a caret
2510: // outside the form component, which is probably where users want components
2511: // added.
2512: // FacesPageUnit unit = webform.getModel().getFacesUnit();
2513: //
2514: // if (unit != null) {
2515: // MarkupBean mb = unit.getDefaultParent();
2516: //// RaveElement element = (RaveElement)mb.getElement();
2517: // Element element = mb.getElement();
2518:
2519: // Element element = webform.getDefaultParentMarkupBeanElement();
2520: // if (element != null) {
2521:
2522: // if (element.getDesignBean() != null) {
2523: //// CssBox box = findBox(element.getDesignBean());
2524: // CssBox box = findBox(webform.getPane().getPageBox(), element.getDesignBean());
2525: // MarkupDesignBean markupDesignBean = InSyncService.getProvider().getMarkupDesignBeanForElement(element);
2526: // MarkupDesignBean markupDesignBean = WebForm.getDomProviderService().getMarkupDesignBeanForElement(element);
2527: // if (markupDesignBean != null) {
2528: // CssBox box = findBox(webform.getPane().getPageBox(), markupDesignBean);
2529:
2530: // XXX This seems to be rendundant (and JSF specific) here.
2531: //// Element componentRootElement = MarkupService.getRenderedElementForElement(element);
2532: // Element componentRootElement = webform.getDefaultParentComponent();
2533: // if (componentRootElement != null) {
2534: // CssBox box = findBoxForComponentRootElement(webform.getPane().getPageBox(), componentRootElement);
2535: //
2536: // if (box != null) {
2537: // LineBox lb = findFirstLineBox(box);
2538: //
2539: // if (lb != null) {
2540: //// Position pos = findFirstLineboxPosition(lb, 0, webform.getManager().getInlineEditor());
2541: // DomPosition pos = findFirstLineboxPosition(lb, 0, webform.getManager().getInlineEditor());
2542: //
2543: //// if (pos != Position.NONE) {
2544: // if (pos != DomPosition.NONE) {
2545: //// if (pos.isRendered()) {
2546: //// if (MarkupService.isRenderedNode(pos.getNode())) {
2547: // if (pos.isRenderedPosition()) {
2548: // return pos.getSourcePosition();
2549: // } else {
2550: // return pos;
2551: // }
2552: // }
2553: // }
2554: // }
2555: // }
2556: // }
2557:
2558: if (webform.getPane().getPageBox() != null) {
2559: LineBox lb = findFirstLineBox(webform.getPane()
2560: .getPageBox());
2561:
2562: if (lb != null) {
2563: // Position pos = findFirstLineboxPosition(lb, 0, webform.getManager().getInlineEditor());
2564: DomPosition pos = findFirstLineboxPosition(lb, 0,
2565: webform.getManager().getInlineEditor());
2566:
2567: // if (pos != Position.NONE) {
2568: if (pos != DomPosition.NONE) {
2569: // if (pos.isRendered()) {
2570: // if (MarkupService.isRenderedNode(pos.getNode())) {
2571: if (pos.isRenderedPosition()) {
2572: return pos.getSourcePosition();
2573: } else {
2574: return pos;
2575: }
2576: }
2577: }
2578: }
2579:
2580: // Try to pick a place to set the caret
2581: // First, if we have a webform, set the caret inside the <h:form> if
2582: // one exists (and has a <br>)
2583: // TODO; this is tricky. We've gotta make sure we only use
2584: // normal flow children, not absolutely positioned children etc.
2585: // assert false : "need layout tree to set caret";
2586: // return Position.NONE;
2587: return DomPosition.NONE;
2588:
2589: /*
2590: Element body = webform.getMarkup().getBody();
2591: NodeList children = body.getChildNodes();
2592: Position pos;
2593: if (children.getLength() > 0 &&
2594: children.item(0).getNodeType() == Node.ELEMENT_NODE &&
2595: ((Element)children.item(0)).getTagName().equals("p")) { // NOI18N
2596: pos = new Position(children.item(0), 0, Bias.FORWARD);
2597: } else if (children.getLength() > 1 &&
2598: children.item(0).getNodeType() == Node.TEXT_NODE &&
2599: Utilities.onlyWhitespace(children.item(0).getNodeValue()) &&
2600: children.item(1).getNodeType() == Node.ELEMENT_NODE &&
2601: ((Element)children.item(1)).getTagName().equals("p")) { // NOI18N
2602: pos = new Position(children.item(1), 0, Bias.FORWARD);
2603: } else {
2604: pos = new Position(webform.getDocument().getBody(), 0, Bias.FORWARD);
2605: }
2606:
2607: */
2608: }
2609:
2610: /**
2611: * Return the last caret position in the document. If create is true,
2612: * create one if necessary.
2613: */
2614: // public static Position getLastDocumentPosition(WebForm webform, boolean create) {
2615: public static DomPosition getLastDocumentPosition(WebForm webform,
2616: boolean create) {
2617:
2618: // XXX #128116 Possible NPE after closing component.
2619: if (webform.getPane().getPageBox() == null) {
2620: return DomPosition.NONE;
2621: }
2622:
2623: // XXX Very suspicious assertion.
2624: // assert webform.getPane().getPageBox().getElement().getOwnerDocument() == webform.getJspDom();
2625: if (webform.getPane().getPageBox().getElement()
2626: .getOwnerDocument() != webform.getHtmlDom()) {
2627: ErrorManager.getDefault().notify(
2628: ErrorManager.INFORMATIONAL,
2629: new IllegalStateException(
2630: "Owner document is expected to be html dom="
2631: + webform.getHtmlDom() // NOI18N
2632: + ", but it is dom="
2633: + webform.getPane().getPageBox()
2634: .getElement()
2635: .getOwnerDocument())); // NOI18N
2636: }
2637:
2638: if (webform.getPane().getPageBox() != null) {
2639: LineBox lb = findLastLineBox(webform.getPane().getPageBox());
2640:
2641: if (lb != null) {
2642: // Position pos = findLastLineboxPosition(lb, lb.getBoxCount() - 1, webform.getManager().getInlineEditor());
2643: DomPosition pos = findLastLineboxPosition(lb, lb
2644: .getBoxCount() - 1, webform.getManager()
2645: .getInlineEditor());
2646:
2647: // if (pos != Position.NONE) {
2648: if (pos != DomPosition.NONE) {
2649: // if (pos.isRendered()) {
2650: // if (MarkupService.isRenderedNode(pos.getNode())) {
2651: if (pos.isRenderedPosition()) {
2652: return pos.getSourcePosition();
2653: } else {
2654: return pos;
2655: }
2656: }
2657: }
2658: }
2659:
2660: // Try to pick a place to set the caret
2661: // First, if we have a webform, set the caret inside the <h:form> if
2662: // one exists (and has a <br>)
2663: // TODO; this is tricky. We've gotta make sure we only use
2664: // normal flow children, not absolutely positioned children etc.
2665: // assert false : "need layout tree to set caret";
2666: // return Position.NONE;
2667: return DomPosition.NONE;
2668:
2669: /*
2670: Element body = webform.getMarkup().getBody();
2671: NodeList children = body.getChildNodes();
2672: Position pos;
2673: if (children.getLength() > 0 &&
2674: children.item(0).getNodeType() == Node.ELEMENT_NODE &&
2675: ((Element)children.item(0)).getTagName().equals("p")) { // NOI18N
2676: pos = new Position(children.item(0), 0, Bias.FORWARD);
2677: } else if (children.getLength() > 1 &&
2678: children.item(0).getNodeType() == Node.TEXT_NODE &&
2679: Utilities.onlyWhitespace(children.item(0).getNodeValue()) &&
2680: children.item(1).getNodeType() == Node.ELEMENT_NODE &&
2681: ((Element)children.item(1)).getTagName().equals("p")) { // NOI18N
2682: pos = new Position(children.item(1), 0, Bias.FORWARD);
2683: } else {
2684: pos = new Position(webform.getDocument().getBody(), 0, Bias.FORWARD);
2685: }
2686:
2687: */
2688: }
2689:
2690: // public static boolean isValidPosition(Position pos, boolean adjust, InlineEditor inline) {
2691: public static boolean isValidPosition(WebForm webForm,
2692: DomPosition pos, boolean adjust, InlineEditor inline) {
2693: // return findValidPosition(pos, adjust, inline) != Position.NONE;
2694: return findValidPosition(webForm, pos, adjust, inline) != DomPosition.NONE;
2695: }
2696:
2697: // XXX Moved from DesignerUtils.
2698: /**
2699: * Given a position in the DOM, find the closest valid position.
2700: * In particular, the position is not allowed to be inside any
2701: * "renders children" nodes. It also doesn't allow positions
2702: * that are "adjacent" (before, after) an absolutely positioned
2703: * element.
2704: *
2705: * @param pos Position to be checked
2706: * @param adjust If true, adjust the position to the nearest (above)
2707: * position that is valid.
2708: * @param inline inlineEditor which is in the game in the designer or null.
2709: * @todo This method is mostly used to determine if a position is a valid
2710: * caret position now. Perhaps rename it to that (isValidCaretPosition).
2711: * @param dom The JSPX document DOM
2712: */
2713: // public static Position checkValidPosition(Position pos, boolean adjust, /*WebForm webform*/InlineEditor inline) {
2714: // public static Position findValidPosition(Position pos, boolean adjust, /*WebForm webform*/InlineEditor inline) {
2715: public static DomPosition findValidPosition(WebForm webForm,
2716: DomPosition pos, boolean adjust, /*WebForm webform*/
2717: InlineEditor inline) {
2718: // if(DEBUG) {
2719: // debugLog(DesignerUtils.class.getName() + ".checkPosition(Position, boolean, WebForm)");
2720: // }
2721: // if(pos == null || webform == null) {
2722: if (pos == null) {
2723: return null;
2724: }
2725: // if (pos == Position.NONE) {
2726: if (pos == DomPosition.NONE) {
2727: return pos;
2728: }
2729:
2730: Node node = pos.getNode();
2731:
2732: if (!adjust) {
2733: // InlineEditor inline = webform.getManager().getInlineEditor();
2734:
2735: if (inline != null) {
2736: if (inline.checkPosition(pos)) {
2737: return pos;
2738: } else {
2739: // return Position.NONE;
2740: return DomPosition.NONE;
2741: }
2742: }
2743: }
2744:
2745: // Don't accept positions adjacent to an absolutely or relatively positioned container
2746: if (!adjust) {
2747: // RaveElement target = pos.getTargetElement();
2748: // if ((target != null) && target.isRendered()) {
2749: Element target = pos.getTargetElement();
2750: // if (MarkupService.isRenderedNode(target)) {
2751: if (webForm.isRenderedNode(target)) {
2752: // Value val = CssLookup.getValue(target, XhtmlCss.POSITION_INDEX);
2753: CssValue cssValue = CssProvider.getEngineService()
2754: .getComputedValueForElement(target,
2755: XhtmlCss.POSITION_INDEX);
2756:
2757: // if ((val == CssValueConstants.ABSOLUTE_VALUE) ||
2758: // (val == CssValueConstants.RELATIVE_VALUE) ||
2759: // (val == CssValueConstants.FIXED_VALUE)) {
2760: if (CssProvider.getValueService().isAbsoluteValue(
2761: cssValue)
2762: || CssProvider.getValueService()
2763: .isRelativeValue(cssValue)
2764: || CssProvider.getValueService().isFixedValue(
2765: cssValue)) {
2766: // return Position.NONE;
2767: return DomPosition.NONE;
2768: }
2769: }
2770: }
2771:
2772: while (node != null) {
2773: // if (node instanceof RaveRenderNode) {
2774: // RaveRenderNode rn = (RaveRenderNode)node;
2775: // if (rn.isRendered() && (rn.getSourceNode() == null)) {
2776: // if (MarkupService.isRenderedNode(node) && MarkupService.getSourceNodeForNode(node) == null) {
2777: if (webForm.isRenderedNode(node)
2778: && MarkupService.getSourceNodeForNode(node) == null) {
2779: if (adjust) {
2780: Node curr = node;
2781:
2782: while (curr != null) {
2783: if (curr.getNodeType() == Node.ELEMENT_NODE) {
2784: // RaveElement e = (RaveElement)curr;
2785: Element e = (Element) curr;
2786:
2787: // if (e.getSource() != null) {
2788: if (MarkupService
2789: .getSourceElementForElement(e) != null) {
2790: // MarkupDesignBean bean = e.getDesignBean();
2791: // MarkupDesignBean bean = InSyncService.getProvider().getMarkupDesignBeanForElement(e);
2792: // MarkupDesignBean bean = WebForm.getDomProviderService().getMarkupDesignBeanForElement(e);
2793: //
2794: // if (bean != null) {
2795: // bean = /*FacesSupport.*/findRendersChildren(bean);
2796: //// e = (RaveElement)bean.getElement();
2797: // e = bean.getElement();
2798: // }
2799: Element se = webForm
2800: .getDomProviderService()
2801: .getSourceElementWhichRendersChildren(
2802: e);
2803: if (se != null) {
2804: e = se;
2805: }
2806:
2807: // return Position.create(e, pos.getOffset() > 0);
2808: return webForm.createDomPosition(e, pos
2809: .getOffset() > 0);
2810: }
2811: }
2812:
2813: curr = curr.getParentNode();
2814:
2815: if (curr == null) {
2816: // return Position.NONE;
2817: return DomPosition.NONE;
2818: }
2819: }
2820: } else {
2821: // return Position.NONE;
2822: return DomPosition.NONE;
2823: }
2824: // }
2825: }
2826:
2827: // if (node instanceof RaveElement) {
2828: // RaveElement element = (RaveElement)node;
2829: if (node instanceof Element) {
2830: Element element = (Element) node;
2831:
2832: // if (element.getDesignBean() != null) {
2833: // MarkupDesignBean bean = element.getDesignBean();
2834: // MarkupDesignBean bean = InSyncService.getProvider().getMarkupDesignBeanForElement(element);
2835: // MarkupDesignBean bean = WebForm.getDomProviderService().getMarkupDesignBeanForElement(element);
2836: //
2837: // if (bean != null) {
2838: // MarkupDesignBean parent = /*FacesSupport.*/findRendersChildren(bean);
2839: //
2840: // // XXX what if bean itself is a renders children?
2841: // if (parent != bean) {
2842: Element se = webForm.getDomProviderService()
2843: .getSourceElementWhichRendersChildren(element);
2844: if (se != null) {
2845: // if (se != element) {
2846: // XXX #112580 Needs to compare rendered with rendered or source with source element.
2847: if (se != MarkupService
2848: .getSourceElementForElement(element)) {
2849: if (adjust) {
2850: // There was a renders-children parent we
2851: // should skip
2852: // Element parentElement = parent.getElement();
2853: Element parentElement = se;
2854:
2855: // return Position.create(parentElement, pos.getOffset() > 0);
2856: return webForm.createDomPosition(
2857: parentElement, pos.getOffset() > 0);
2858: } else {
2859: // return Position.NONE;
2860: return DomPosition.NONE;
2861: }
2862: }
2863:
2864: break;
2865: }
2866: }
2867:
2868: node = node.getParentNode();
2869: }
2870:
2871: // InlineEditor inline = webform.getManager().getInlineEditor();
2872:
2873: // if (((pos != Position.NONE) && ((inline != null) && inline.checkPosition(pos))) ||
2874: if (((pos != DomPosition.NONE) && ((inline != null) && inline
2875: .checkPosition(pos)))
2876: ||
2877: // !pos.isRendered()) {
2878: // !MarkupService.isRenderedNode(pos.getNode())) {
2879: pos.isSourcePosition()) {
2880: return pos;
2881: } else if (adjust) {
2882: // Try to find the corresponding source
2883: node = pos.getNode();
2884:
2885: while (node != null) {
2886: // if (node instanceof RaveElement) {
2887: // RaveElement element = (RaveElement)node;
2888: if (node instanceof Element) {
2889: Element element = (Element) node;
2890:
2891: // if (element.getDesignBean() != null) {
2892: // DesignBean bean = element.getDesignBean();
2893: // DesignBean bean = InSyncService.getProvider().getMarkupDesignBeanForElement(element);
2894: // DesignBean bean = WebForm.getDomProviderService().getMarkupDesignBeanForElement(element);
2895: // if (bean != null) {
2896: Element componentRootElement = webForm
2897: .getDomProviderService()
2898: .getComponentRootElementForElement(element);
2899: if (componentRootElement != null) {
2900: // Element el = FacesSupport.getElement(bean);
2901: // Element el = Util.getElement(bean);
2902: // Element el = WebForm.getDomProviderService().getElement(bean);
2903: Element sourceElement = MarkupService
2904: .getSourceElementForElement(componentRootElement);
2905: // return Position.create(sourceElement, pos.getOffset() > 0);
2906: return webForm.createDomPosition(sourceElement,
2907: pos.getOffset() > 0);
2908: }
2909: }
2910:
2911: node = node.getParentNode();
2912: }
2913:
2914: // return Position.NONE;
2915: return DomPosition.NONE;
2916: } else {
2917: // // XXX shouldn't this be return pos; ? Try to click somewhere in BoxModelTest
2918: // // layout-floats3.html
2919: // return Position.NONE;
2920: // }
2921: return pos;
2922: }
2923: }
2924:
2925: // // XXX Moved from FacesSupport.
2926: // /** Find outermost renders-children bean above the given bean, or
2927: // * the bean itself if there is no such parent.
2928: // */
2929: // private /*public*/ static MarkupDesignBean findRendersChildren(MarkupDesignBean bean) {
2930: // // Similar to FacesSupport.findHtmlContainer(bean), but
2931: // // we need to return the outermost html container itself, not
2932: // // the parent, since we're not looking for its container but
2933: // // the bean to be moved itself.
2934: // MarkupDesignBean curr = bean;
2935: //
2936: //// for (; curr != null; curr = FacesSupport.getBeanParent(curr)) {
2937: // for (; curr != null; curr = getBeanParent(curr)) {
2938: // if (curr.getInstance() instanceof F_Verbatim) {
2939: // // If you have a verbatim, we're okay to add html comps below it
2940: // return bean;
2941: // }
2942: //
2943: // if (curr.getInstance() instanceof UIComponent) {
2944: // // Need to set the Thread's context classloader to be the Project's ClassLoader.
2945: // ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
2946: // try {
2947: //// Thread.currentThread().setContextClassLoader(InSyncService.getProvider().getContextClassLoader(curr));
2948: // Thread.currentThread().setContextClassLoader(WebForm.getDomProviderService().getContextClassLoaderForDesignContext(curr.getDesignContext()));
2949: // if (((UIComponent)curr.getInstance()).getRendersChildren()) {
2950: // bean = curr;
2951: // // Can't break here - there could be an outer
2952: // // renders-children parent
2953: // }
2954: // } finally {
2955: // Thread.currentThread().setContextClassLoader(oldContextClassLoader);
2956: // }
2957: // }
2958: // }
2959: //
2960: // return bean;
2961: // }
2962: //
2963: // // XXX Moved from FacesSupport.
2964: // /**
2965: // * Return the parent of the given markup design bean, if the parent is
2966: // * a MarkupDesignBean.
2967: // */
2968: // private static MarkupDesignBean getBeanParent(MarkupDesignBean bean) {
2969: // DesignBean parent = bean.getBeanParent();
2970: //
2971: // if (parent instanceof MarkupDesignBean) {
2972: // return (MarkupDesignBean)parent;
2973: // }
2974: //
2975: // return null;
2976: // }
2977:
2978: /** Try to find the box corresponding to the given element. */
2979: public static CssBox findBox(PageBox pageBox, Element element) {
2980: // CssBox box = CssBox.getBox(element);
2981: CssBox box = pageBox.getWebForm().findCssBoxForElement(element);
2982:
2983: if (box != null) {
2984: return box;
2985: }
2986:
2987: // Position pos = Position.create(element, false);
2988: DomPosition pos = pageBox.getWebForm().createDomPosition(
2989: element, false);
2990:
2991: // if (!pos.isRendered()) {
2992: // if (!MarkupService.isRenderedNode(pos.getNode())) {
2993: if (pos.isSourcePosition()) {
2994: pos = pos.getRenderedPosition();
2995: }
2996:
2997: // if (pos == Position.NONE) {
2998: if (pos == DomPosition.NONE) {
2999: return null;
3000: }
3001:
3002: // box = findBox(webform.getPane().getPageBox(), pos);
3003: box = findBox(pageBox, pos);
3004:
3005: if (box != null) {
3006: return box;
3007: }
3008:
3009: return null;
3010: }
3011:
3012: // /** Locate a component in the visible view given the x,y coordinates
3013: // * XXX Get rid of it, replace with #findElement. */
3014: // public static MarkupDesignBean findMarkupDesignBean(CssBox box) {
3015: // for (; box != null; box = box.getParent()) {
3016: // MarkupDesignBean boxMarkupDesignBean = CssBox.getMarkupDesignBeanForCssBox(box);
3017: //// if (box.getDesignBean() != null) {
3018: //// DesignBean lb = box.getDesignBean();
3019: // if (boxMarkupDesignBean != null) {
3020: // DesignBean lb = boxMarkupDesignBean;
3021: //
3022: //// if (FacesSupport.isSpecialBean(/*webform, */lb)) {
3023: //// if (Util.isSpecialBean(lb)) {
3024: // if (lb instanceof MarkupDesignBean && WebForm.getDomProviderService().isSpecialComponent(
3025: // WebForm.getDomProviderService().getComponentRootElementForMarkupDesignBean((MarkupDesignBean)lb))) {
3026: // continue;
3027: // }
3028: //
3029: // return boxMarkupDesignBean;
3030: // }
3031: // }
3032: //
3033: // return null;
3034: // }
3035:
3036: /** Locates element in the visible view given the box.
3037: * @return <code>Element</code> or <code>null</code> if there is not such */
3038: public static Element findElement(CssBox box) {
3039: WebForm webForm = box == null ? null : box.getWebForm();
3040: if (webForm == null) {
3041: return null;
3042: }
3043: for (; box != null; box = box.getParent()) {
3044: // #107084 Find the element, don't check for the root.
3045: // Element componentRootElement = CssBox.getElementForComponentRootCssBox(box);
3046: Element element = box.getElement();
3047: if (element != null) {
3048: if (webForm.getDomProviderService().isSpecialComponent(
3049: element)) {
3050: continue;
3051: }
3052:
3053: return element;
3054: }
3055: }
3056:
3057: return null;
3058: }
3059:
3060: // XXX #106870 This is different from findElement.
3061: /** Locates component root element in the visible view given the box.
3062: * @return <code>Element</code> or <code>null</code> if there is not such */
3063: public static Element findComponentRootElement(CssBox box) {
3064: WebForm webForm = box == null ? null : box.getWebForm();
3065: if (webForm == null) {
3066: return null;
3067: }
3068: for (; box != null; box = box.getParent()) {
3069: // #107084 Find the element, don't check for the root.
3070: // Element componentRootElement = CssBox.getElementForComponentRootCssBox(box);
3071: // Element element = box.getComponentRootElement();
3072: // if (element != null) {
3073: // XXX #113773 Fixing selecting of some element whose parent box
3074: // is excluded from the hierarchy (suspicous architecture).
3075: Element element = box.getElement();
3076: // XXX #118287 Also pass the parent box to determine the principal element
3077: // (the real one might be excluded from the tree)
3078: CssBox parentBox = box.getParent();
3079: Element parentElement = parentBox == null ? null
3080: : parentBox.getElement();
3081: if (webForm.getDomProviderService().isPrincipalElement(
3082: element, parentElement)) {
3083: if (webForm.getDomProviderService().isSpecialComponent(
3084: element)) {
3085: continue;
3086: }
3087:
3088: return element;
3089: }
3090: }
3091:
3092: return null;
3093: }
3094:
3095: public static CssBox findBox(PageBox pageBox, int x, int y) {
3096: // PageBox pageBox = webform.getPane().getPageBox();
3097:
3098: // Locate closest box
3099: return pageBox.findCssBox(x, y);
3100: }
3101:
3102: // /** XXX Get rid of it, replace with #findElement. */
3103: // public static MarkupDesignBean findMarkupDesignBean(PageBox pageBox, int x, int y) {
3104: //// CssBox box = findBox(x, y);
3105: // CssBox box = findBox(pageBox, x, y);
3106: //
3107: // return findMarkupDesignBean(box);
3108: // }
3109:
3110: public static Element findElement(PageBox pageBox, int x, int y) {
3111: // CssBox box = findBox(x, y);
3112: CssBox box = findBox(pageBox, x, y);
3113:
3114: return findElement(box);
3115: }
3116:
3117: // public static Rectangle findShape(PageBox pageBox, DesignBean lbean) {
3118: public static Rectangle findShape(PageBox pageBox,
3119: Element componentRootElement) {
3120: // CssBox box = findBox(pageBox, lbean);
3121: // if (!(lbean instanceof MarkupDesignBean)) {
3122: // return null;
3123: // }
3124: if (componentRootElement == null) {
3125: return null;
3126: }
3127: CssBox box = findBoxForComponentRootElement(pageBox,
3128: componentRootElement);
3129:
3130: if (box == null) {
3131: ErrorManager.getDefault().notify(
3132: ErrorManager.INFORMATIONAL,
3133: // new NullPointerException("Null box for bean=" + lbean)); // NOI18N
3134: new NullPointerException("Null box for element="
3135: + componentRootElement)); // NOI18N
3136:
3137: return null;
3138: }
3139:
3140: return new Rectangle(box.getAbsoluteX(), box.getAbsoluteY(),
3141: box.getWidth(), box.getHeight());
3142: }
3143:
3144: // // XXX Get rid of this. Replace with #findCssBoxForComponentRootElement.
3145: // public static CssBox findBox(PageBox pageBox, DesignBean lbean) {
3146: //// PageBox pageBox = webform.getPane().getPageBox();
3147: //// return pageBox.find(lbean);
3148: // return pageBox.findCssBox(lbean);
3149: // }
3150:
3151: public static CssBox findBoxForComponentRootElement(
3152: PageBox pageBox, Element componentRootElement) {
3153: // PageBox pageBox = webform.getPane().getPageBox();
3154: // return pageBox.find(lbean);
3155: return pageBox
3156: .findCssBoxForComponentRootElement(componentRootElement);
3157: }
3158:
3159: public static List<Rectangle> getComponentRectangles(
3160: PageBox pageBox, /*DesignBean lb*/
3161: Element componentRootElement) {
3162: List<Rectangle> result = new ArrayList<Rectangle>();
3163: // PageBox pageBox = webform.getPane().getPageBox();
3164: // pageBox.computeRectangles(lb, result);
3165: pageBox.computeRectangles(componentRootElement, result);
3166:
3167: if (result.size() == 0) {
3168: // Didn't find any bounds for the component. That can happen
3169: // with some components, like TableRowGroups, which
3170: // correspond to elements that don't have direct
3171: // representatives as boxes, such as <tr> in tables
3172: // If so, just try to compute its bounds and return
3173: // it here
3174: // Rectangle r = getComponentBounds(lb);
3175: // Rectangle r = getComponentBounds(pageBox, lb);
3176: Rectangle r = getComponentBounds(pageBox,
3177: componentRootElement);
3178:
3179: if (r != null) {
3180: result.add(r);
3181: }
3182: }
3183:
3184: return result;
3185: }
3186:
3187: public static Rectangle getComponentBounds(PageBox pageBox, /*DesignBean lb*/
3188: Element componentRootElement) {
3189: // PageBox pageBox = webform.getPane().getPageBox();
3190: // if (!(lb instanceof MarkupDesignBean)) {
3191: // return null;
3192: // }
3193: // Element componentRootElement = SelectionManager.getComponentRootElementForMarkupDesignBean((MarkupDesignBean)lb);
3194: if (componentRootElement == null) {
3195: return null;
3196: }
3197:
3198: // Rectangle bounds = pageBox.computeBounds(lb, null);
3199: Rectangle bounds = pageBox.computeBounds(componentRootElement,
3200: null);
3201:
3202: // Some components render to a top level element which isn't represented
3203: // directly in the box hierarchy, such as <tr> for example; TableBoxes
3204: // don't have row boxes, they manage cells directly.
3205: // In this case, look for the bounds of all the children of the component
3206: // and assume that the parent is basically made up of the union of its children
3207: if (bounds == null) {
3208: // for (int i = 0, n = lb.getChildBeanCount(); i < n; i++) {
3209: // DesignBean child = lb.getChildBean(i);
3210: // bounds = pageBox.computeBounds(child, bounds);
3211: // }
3212: WebForm webForm = pageBox.getWebForm();
3213: Element[] childComponentRootElements = webForm
3214: .getDomProviderService()
3215: .getChildComponentRootElements(componentRootElement);
3216: for (Element child : childComponentRootElements) {
3217: bounds = pageBox.computeBounds(child, bounds);
3218: }
3219: }
3220:
3221: return bounds;
3222: }
3223:
3224: // public Rectangle getRegionBounds(MarkupMouseRegion region) {
3225: // return webform.getPane().getPageBox().computeRegionBounds(region, null);
3226: // }
3227:
3228: /** Finds closes parent (including itself) component root element. */
3229: public static Element findClosestComponentRootElement(
3230: WebForm webForm, Node node) {
3231: while (node != null) {
3232: if (node instanceof Element
3233: && webForm.getDomProviderService()
3234: .isPrincipalElement((Element) node, null)) {
3235: return (Element) node;
3236: }
3237:
3238: node = node.getParentNode();
3239: }
3240:
3241: return null;
3242: }
3243: }
|