/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.ui.decorations;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.xmind.gef.draw2d.IAnchor;
import org.xmind.gef.draw2d.ITextFigure;
import org.xmind.gef.draw2d.ITitledFigure;
import org.xmind.gef.draw2d.decoration.IShadowedDecoration;
import org.xmind.gef.draw2d.decoration.PathConnectionDecoration;
import org.xmind.gef.draw2d.geometry.Geometry;
import org.xmind.gef.draw2d.geometry.PrecisionPoint;
import org.xmind.gef.draw2d.graphics.Path;
import org.xmind.ui.style.Styles;
public abstract class AbstractRelationshipDecoration
extends PathConnectionDecoration
implements IRelationshipDecoration, IShadowedDecoration {
private static final Rectangle CLIP = new Rectangle();
protected Double sourceCPAngle = null;
protected Double sourceCPAmount = null;
protected Double targetCPAngle = null;
protected Double targetCPAmount = null;
protected Point relativeSourceCP = null;
protected Point relativeTargetCP = null;
private PrecisionPoint sourceCP = null;
private PrecisionPoint targetCP = null;
private IArrowDecoration arrow1 = null;
private IArrowDecoration arrow2 = null;
private PrecisionPoint titlePos = null;
protected AbstractRelationshipDecoration() {
}
protected AbstractRelationshipDecoration(String id) {
super(id);
}
protected int getLineWidthForChecking() {
return super.getLineWidthForChecking() * 3 + 10;
}
public void reroute(IFigure figure) {
super.reroute(figure);
updateArrows(figure);
}
protected void reroute(IFigure figure, PrecisionPoint sourcePos,
PrecisionPoint targetPos, boolean validating) {
PrecisionPoint oldSourceCP = this.sourceCP;
PrecisionPoint oldTargetCP = this.targetCP;
PrecisionPoint oldTitlePos = this.titlePos;
PrecisionPoint newSourceCP = new PrecisionPoint();
PrecisionPoint newTargetCP = new PrecisionPoint();
PrecisionPoint newTitlePos = new PrecisionPoint();
reroute(figure, sourcePos, targetPos, newSourceCP, newTargetCP);
calcTitlePosition(figure, newTitlePos, sourcePos, targetPos,
newSourceCP, newTargetCP);
this.sourceCP = newSourceCP;
this.targetCP = newTargetCP;
this.titlePos = newTitlePos;
if (!validating && figure != null) {
if (!newSourceCP.equals(oldSourceCP)
|| !newTargetCP.equals(oldTargetCP)
|| !newTitlePos.equals(oldTitlePos)) {
figure.revalidate();
repaint(figure);
}
}
}
protected void reroute(IFigure figure, PrecisionPoint sourcePos,
PrecisionPoint targetPos, PrecisionPoint sourceCP,
PrecisionPoint targetCP) {
IAnchor sa = getSourceAnchor();
IAnchor ta = getTargetAnchor();
// if (sa != null)
// sourcePos.setLocation(sa.getReferencePoint());
// if (ta != null)
// targetPos.setLocation(ta.getReferencePoint());
PrecisionPoint a1 = null;
PrecisionPoint a2 = null;
if (sa != null) {
if (relativeSourceCP != null) {
sourceCP.setLocation(sa.getReferencePoint())
.translate(relativeSourceCP.x, relativeSourceCP.y);
sourcePos.setLocation(sa.getLocation(sourceCP, 0));
} else if (ta != null) {
if (a1 == null)
a1 = sa.getLocation(ta.getReferencePoint(), 0);
sourcePos.setLocation(a1);
if (a2 == null)
a2 = ta.getLocation(sa.getReferencePoint(), 0);
sourceCP.setLocation(a1).move(a2,
Styles.DEF_CONTROL_POINT_AMOUNT);
}
}
if (ta != null) {
if (relativeTargetCP != null) {
targetCP.setLocation(ta.getReferencePoint())
.translate(relativeTargetCP.x, relativeTargetCP.y);
targetPos.setLocation(ta.getLocation(targetCP, 0));
} else if (sa != null) {
if (a2 == null)
a2 = ta.getLocation(sa.getReferencePoint(), 0);
targetPos.setLocation(a2);
if (a1 == null)
a1 = sa.getLocation(ta.getReferencePoint(), 0);
targetCP.setLocation(a2).move(a1,
Styles.DEF_CONTROL_POINT_AMOUNT);
}
}
// double dx = targetPos.x - sourcePos.x;
// double dy = targetPos.y - sourcePos.y;
// double angle1 = getAngleValue(sourceCPAngle);
// double theta1 = Geometry.getAngle(dx, dy) + angle1;
// sourceCP.setLocation(sourcePos).move(theta1, 100);
//
// double angle2 = getAngleValue(targetCPAngle);
// double theta2 = Geometry.getAngle(-dx, -dy) + angle2;
// targetCP.setLocation(targetPos).move(theta2, 100);
//
// if (sa != null)
// sourcePos.setLocation(sa.getLocation(sourceCP, 0));
// if (ta != null)
// targetPos.setLocation(ta.getLocation(targetCP, 0));
//
// dx = targetPos.x - sourcePos.x;
// dy = targetPos.y - sourcePos.y;
// double d = Math.max(Math.hypot(dx, dy), Geometry.MIN_DISTANCE);
//
// double amount1 = getAmountValue(sourceCPAmount);
// sourceCP.setLocation(sourcePos).move(theta1, d * amount1);
//
// double amount2 = getAmountValue(targetCPAmount);
// targetCP.setLocation(targetPos).move(theta2, d * amount2);
}
protected void updatePosAndCp(IAnchor anchor, PrecisionPoint pos,
PrecisionPoint cp, double angle) {
Point center = anchor.getReferencePoint().toDraw2DPoint();
Point rotatedCp = Geometry.getRotatedPoint(
new Point(cp.toDraw2DPoint()).translate(-center.x, -center.y),
angle);
cp.setLocation(rotatedCp.translate(center.x, center.y));
pos.setLocation(anchor.getLocation(cp, 0));
}
protected void calcTitlePosition(IFigure figure, PrecisionPoint titlePos,
PrecisionPoint sourcePos, PrecisionPoint targetPos,
PrecisionPoint sourceCP, PrecisionPoint targetCP) {
titlePos.setLocation((sourcePos.x + targetPos.x) / 2,
(sourcePos.y + targetPos.y) / 2);
}
// private double getAngleValue(Double angle) {
// return angle == null ? Styles.DEF_CONTROL_POINT_ANGLE : angle
// .doubleValue();
// }
//
// private double getAmountValue(Double amount) {
// return amount == null ? Styles.DEF_CONTROL_POINT_AMOUNT : amount
// .doubleValue();
// }
protected boolean isPositionValid() {
return super.isPositionValid() && sourceCP != null && targetCP != null
&& titlePos != null;
}
public PrecisionPoint getSourceControlPoint(IFigure figure) {
checkValidation(figure);
return sourceCP;
}
public PrecisionPoint getTargetControlPoint(IFigure figure) {
checkValidation(figure);
return targetCP;
}
public void setRelativeSourceControlPoint(IFigure figure, Point point) {
if (point == this.relativeSourceCP
|| (point != null && point.equals(this.relativeSourceCP)))
return;
this.relativeSourceCP = point;
if (figure != null) {
figure.revalidate();
figure.repaint();
}
invalidate();
}
public void setRelativeTargetControlPoint(IFigure figure, Point point) {
if (point == this.relativeTargetCP
|| (point != null && point.equals(this.relativeTargetCP)))
return;
this.relativeTargetCP = point;
if (figure != null) {
figure.revalidate();
figure.repaint();
}
invalidate();
}
public void setSourceControlPointHint(IFigure figure, Double angle,
Double amount) {
boolean changed = false;
if (angle != this.sourceCPAngle
&& (angle == null || !angle.equals(this.sourceCPAngle))) {
changed = true;
this.sourceCPAngle = angle;
}
if (amount != this.sourceCPAmount
&& (amount == null || !amount.equals(this.sourceCPAmount))) {
changed = true;
this.sourceCPAmount = amount;
}
if (changed && figure != null) {
figure.revalidate();
repaint(figure);
}
}
public void setTargetControlPointHint(IFigure figure, Double angle,
Double amount) {
boolean changed = false;
if (angle != this.targetCPAngle
&& (angle == null || !angle.equals(this.targetCPAngle))) {
changed = true;
this.targetCPAngle = angle;
}
if (amount != this.targetCPAmount
&& (amount == null || !amount.equals(this.targetCPAmount))) {
changed = true;
this.targetCPAmount = amount;
}
if (changed && figure != null) {
figure.revalidate();
repaint(figure);
}
}
// public void setOrthogonalSourceControlPointHint(IFigure figure,
// PrecisionPoint pos) {
// }
//
// public void setOrthogonalTargetControlPointHint(IFigure figure,
// PrecisionPoint pos) {
// }
//
// public Double getPolarSourceControlPointAmountHint() {
// return sourceCPAmount;
// }
//
// public Double getPolarSourceControlPointAngleHint() {
// return sourceCPAngle;
// }
protected double getSourceAnchorAngle(IFigure figure) {
PrecisionPoint p1 = getSourcePosition(figure);
PrecisionPoint p2 = getSourceControlPoint(figure);
return Geometry.getAngle(p2, p1);
}
protected double getTargetAnchorAngle(IFigure figure) {
PrecisionPoint p1 = getTargetPosition(figure);
PrecisionPoint p2 = getTargetControlPoint(figure);
return Geometry.getAngle(p2, p1);
}
public IArrowDecoration getArrow1() {
return arrow1;
}
public IArrowDecoration getArrow2() {
return arrow2;
}
public void setArrow1(IFigure figure, IArrowDecoration arrow) {
if (arrow == this.arrow1)
return;
this.arrow1 = arrow;
if (figure != null) {
figure.revalidate();
repaint(figure);
}
}
public void setArrow2(IFigure figure, IArrowDecoration arrow) {
if (arrow == this.arrow2)
return;
this.arrow2 = arrow;
if (figure != null) {
figure.revalidate();
repaint(figure);
}
}
@Override
public void invalidate() {
if (arrow1 != null)
arrow1.invalidate();
if (arrow2 != null)
arrow2.invalidate();
super.invalidate();
}
public void validate(IFigure figure) {
super.validate(figure);
if (arrow1 != null)
arrow1.validate(figure);
if (arrow2 != null)
arrow2.validate(figure);
}
public void paint(IFigure figure, Graphics graphics) {
super.paint(figure, graphics);
if (arrow1 != null)
arrow1.paint(figure, graphics);
if (arrow2 != null)
arrow2.paint(figure, graphics);
}
public Rectangle getPreferredBounds(IFigure figure) {
Rectangle r = super.getPreferredBounds(figure);
if (arrow1 != null)
r = r.getUnion(arrow1.getPreferredBounds(figure));
if (arrow2 != null)
r = r.getUnion(arrow2.getPreferredBounds(figure));
return r;
}
public PrecisionPoint getTitlePosition(IFigure figure) {
checkValidation(figure);
return titlePos;
}
private void updateArrows(IFigure figure) {
if (arrow1 != null) {
arrow1.setPosition(figure, getSourcePosition(figure));
arrow1.setAngle(figure, getSourceAnchorAngle(figure));
arrow1.reshape(figure);
}
if (arrow2 != null) {
arrow2.setPosition(figure, getTargetPosition(figure));
arrow2.setAngle(figure, getTargetAnchorAngle(figure));
arrow2.reshape(figure);
}
}
public void paintShadow(IFigure figure, Graphics graphics) {
if (!isVisible())
return;
checkValidation(figure);
graphics.setAlpha(getAlpha());
graphics.setForegroundColor(ColorConstants.black);
graphics.setLineWidth(getLineWidth());
graphics.setLineStyle(getLineStyle());
drawLine(figure, graphics);
}
protected void paintPath(IFigure figure, Graphics graphics, Path path,
boolean fill) {
ITextFigure tf = getTitleFigure(figure);
if (tf != null && tf.isShowing()) {
Rectangle bounds = figure.getBounds();
Rectangle titleArea = tf.getBounds();
if (titleArea.intersects(bounds)) {
graphics.pushState();
try {
paintPathAroundTitle(figure, graphics, path, fill, bounds,
titleArea);
} finally {
graphics.popState();
}
return;
}
}
super.paintPath(figure, graphics, path, fill);
}
private void paintPathAroundTitle(IFigure figure, Graphics graphics,
Path path, boolean fill, Rectangle bounds, Rectangle titleArea) {
// clip the top part
int w = bounds.width;
int h = titleArea.y - bounds.y;
if (w > 0 && h > 0) {
CLIP.setSize(w, h);
CLIP.setLocation(bounds.x, bounds.y);
paintPathWithClip(figure, graphics, path, fill, CLIP);
}
// clip the bottom part
w = bounds.width;
h = bounds.y + bounds.height - titleArea.y - titleArea.height;
if (w > 0 && h > 0) {
CLIP.setSize(w, h);
CLIP.setLocation(bounds.x, titleArea.y + titleArea.height);
paintPathWithClip(figure, graphics, path, fill, CLIP);
}
// clip the left part
w = titleArea.x - bounds.x;
h = titleArea.height;
if (w > 0 && h > 0) {
CLIP.setSize(w, h);
CLIP.setLocation(bounds.x, titleArea.y);
paintPathWithClip(figure, graphics, path, fill, CLIP);
}
// clip the right part
w = bounds.x + bounds.width - titleArea.x - titleArea.width;
if (w > 0 && h > 0) {
CLIP.setSize(w, h);
CLIP.setLocation(titleArea.x + titleArea.width, titleArea.y);
paintPathWithClip(figure, graphics, path, fill, CLIP);
}
CLIP.setBounds(titleArea);
int alpha = graphics.getAlpha();
graphics.setAlpha((int) (alpha * 0.1));
paintPathWithClip(figure, graphics, path, fill, CLIP);
}
protected void paintPathWithClip(IFigure figure, Graphics graphics,
Path path, boolean fill, Rectangle clip) {
graphics.setClip(clip);
super.paintPath(figure, graphics, path, fill);
graphics.restoreState();
}
protected ITextFigure getTitleFigure(IFigure figure) {
if (figure instanceof ITitledFigure) {
return ((ITitledFigure) figure).getTitle();
}
return null;
}
}