blob: 27aae6901a926d22a92b59aade5fc991fc1121c7 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}