/******************************************************************************* * Copyright (c) 2006 Sybase, Inc. and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Sybase, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.jst.pagedesigner.viewer; import java.util.List; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.EditPart; import org.eclipse.jst.pagedesigner.IHTMLConstants; import org.eclipse.jst.pagedesigner.dom.EditModelQuery; import org.eclipse.jst.pagedesigner.validation.caret.IMovementMediator; import org.eclipse.jst.pagedesigner.validation.caret.IPositionMediator; import org.eclipse.jst.pagedesigner.validation.caret.Target; import org.w3c.dom.Node; /** * @author mengbo * @version 1.5 */ public class CaretPositionResolver { private IPositionMediator _validator; private Point _point; private static CaretPositionResolver _instance; /** * @param validator * @param point * @return the singleton instance */ public static CaretPositionResolver getInstance( IPositionMediator validator, Point point) { if (_instance == null) { _instance = new CaretPositionResolver(); } _instance.setPoint(point); _instance.setValidator(validator); return _instance; } /** * @param _point * The _point to set. */ private void setPoint(Point _point) { this._point = _point; } /** * @param _validator * The _validator to set. */ private void setValidator(IPositionMediator _validator) { this._validator = _validator; } /** * Calculate the two part's distance to point, the shorter one will be * return. Distance is calculated based on: if there is one box contains * point.y, then calculate that box, or if there is no any such one box, * then calculate the y distance. * * @param part1 * @param part2 * @param point * @return */ static LayoutPart getCloserPart(LayoutPart part1, LayoutPart part2, Point _point) { if (part1 == null || EditModelQuery.isTransparentText(Target.resolveNode(part1 .getPart()))) { return part2; } else if (part2 == null || EditModelQuery.isTransparentText(Target.resolveNode(part2 .getPart()))) { return part1; } Rectangle rect1 = part1.getAbsoluteBounds(); Rectangle rect2 = part2.getAbsoluteBounds(); Node n1 = Target.resolveNode(part1.getPart()); Node n2 = Target.resolveNode(part2.getPart()); // Within same. if (EditModelQuery.isChild(n1, n2) && (CaretPositionResolver.getXDistance(rect2, _point) == 0) && !part1.isCloseToEdgeFromOutSide()) { return part2; } else if (EditModelQuery.isChild(n2, n1) && (CaretPositionResolver.getXDistance(rect1, _point) == 0 && !part2 .isCloseToEdgeFromOutSide()) && !part2.isCloseToEdgeFromOutSide()) { return part1; } if (rect1.intersect(new Rectangle(rect1.x, rect2.y, rect1.width, rect2.height)).height == 0) { return heightFirst(part1, part2, _point); } return widthFirst(part1, part2, _point); } private static LayoutPart heightFirst(LayoutPart part1, LayoutPart part2, Point _point) { Rectangle rect1 = part1.getAbsoluteBounds(); Rectangle rect2 = part2.getAbsoluteBounds(); int offset1 = Math.abs(CaretPositionResolver .getYDistance(rect1, _point)); int offset2 = Math.abs(CaretPositionResolver .getYDistance(rect2, _point)); if (offset1 > offset2) { return part2; } else if (offset1 < offset2) { return part1; } else { offset1 = Math.abs(CaretPositionResolver .getXDistance(rect1, _point)); offset2 = Math.abs(CaretPositionResolver .getXDistance(rect2, _point)); if (offset1 >= offset2) { return part2; } return part1; } } private static LayoutPart widthFirst(LayoutPart part1, LayoutPart part2, Point _point) { Rectangle rect1 = part1.getAbsoluteBounds(); Rectangle rect2 = part2.getAbsoluteBounds(); int offset1 = Math.abs(CaretPositionResolver .getXDistance(rect1, _point)); int offset2 = Math.abs(CaretPositionResolver .getXDistance(rect2, _point)); if (offset1 > offset2) { return part2; } else if (offset1 < offset2) { return part1; } else { offset1 = Math.abs(CaretPositionResolver .getYDistance(rect1, _point)); offset2 = Math.abs(CaretPositionResolver .getYDistance(rect2, _point)); if (offset1 >= offset2) { return part2; } return part1; } } /** * Return a descendent part under parent that is one of the closest part to * point. * * @param parent * @return */ private LayoutPart getClosestChildPart(LayoutPart parent) { LayoutPart result = null; if (parent != null) { List children = null; if ((children = parent.getPart().getChildren()).size() > 0) // && // _validator.hasEditableArea(new // Target(parent.getPart()))) { // iterate its children, we know the part doesn't contain p. so // we only see if its children can be // referenced. for (int i = 0, n = children.size(); i < n; i++) { LayoutPart nextPart = new LayoutPart((EditPart) children .get(i), _point); Target target = new Target(nextPart.getPart()); if (_validator.isValidPosition(new DesignRefPosition(target .getPart(), false))) { result = getCloserPart(result, nextPart, _point); } else if (_validator.hasEditableArea(target)) { LayoutPart temp = getClosestChildPart(nextPart); if (temp == null) { temp = nextPart; } result = getCloserPart(result, temp, _point); } } } } return result; } /** * Use by vertical movement, we need to see whther the par * * @param closestPart * @param target * @return */ LayoutPart resolveClosestPartFrom(LayoutPart closestPart) { Target target = new Target(closestPart.getPart()); LayoutPart finalPart = null; if (EditModelQuery.isInline(Target.resolveNode(closestPart.getPart()))) { if (closestPart.isAfterPoint() || closestPart.isBeforePoint()) { finalPart = closestPart; } else { if (_validator.hasEditableArea(target) && (_validator instanceof IMovementMediator && ((IMovementMediator) _validator) .allowsMoveIn(target) || !(_validator instanceof IMovementMediator))) { finalPart = getClosestChildPartOrPart(closestPart); } } } // block else { if (closestPart.contains(_point)) { if (_validator.hasEditableArea(target) && // (_validator instanceof IMovementMediator && ((IMovementMediator) _validator) .allowsMoveIn(target) || !(_validator instanceof IMovementMediator))) { finalPart = getClosestChildPartOrPart(closestPart); } } // outside of bounds else { if (_validator.hasEditableArea(target) && !IHTMLConstants.TAG_TABLE.equalsIgnoreCase(target .getNode().getNodeName()) && (_validator instanceof IMovementMediator && ((IMovementMediator) _validator) .allowsMoveIn(target) || !(_validator instanceof IMovementMediator))) { if (closestPart.atSameRow(_point)) { finalPart = getClosestChildPartOrPart(closestPart); } else if (!_validator .isValidPosition(new DesignRefPosition(target .getPart(), true))) { finalPart = getClosestChildPartOrPart(closestPart); } } } } if (finalPart == null && // (_validator.isValidPosition(new DesignRefPosition(target .getPart(), true)) || // _validator.isValidPosition(new DesignRefPosition(target .getPart(), false)))) { finalPart = closestPart; } return finalPart; } private LayoutPart getClosestChildPartOrPart(LayoutPart closestPart) { LayoutPart result = getClosestChildPart(closestPart); if (result != null) { result = resolveClosestPartFrom(result); } else { if (closestPart.getConcretePart() == null) { result = closestPart; } } return result; } /** * Get the distance from rect's edge to point.x. * * @param rect * @param point * @return the X distance */ public static int getXDistance(Rectangle rect, Point point) { if (rect.getRight().x <= point.x) { return point.x - (rect.getRight().x); } else if (rect.x >= point.x) { return point.x - rect.x; } else if (rect.x <= point.x && point.x <= rect.getRight().x) { return 0; } return -1; } /** * from point to middle's distance. If the result is nagative, point is at * left part of rect, if it is positive, the point is at the right part. * * @param rect * @param point * @return the X distance */ public static int toXMiddle(Rectangle rect, Point point) { return (point.x - (rect.x + rect.getRight().x) / 2); } /** * from point to middle's distance If the result is nagative, point is at * upper part of rect, if it is positive, the point is at the lower part. * * @param rect * @param point * @return the Y distance */ public static int toYMiddle(Rectangle rect, Point point) { return (point.y - (rect.y + rect.getBottom().y) / 2); } /** * @param rect * @param point * @return the Y distance */ public static int getYDistance(Rectangle rect, Point point) { if (rect.y + rect.height <= point.y) { return point.y - (rect.y + rect.height); } else if (rect.y >= point.y) { return point.y - rect.y; } else if (rect.y <= point.y && point.y <= rect.y + rect.height) { return 0; } return -1; } }