/* ****************************************************************************** * 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.PositionConstants; import org.eclipse.draw2d.geometry.Insets; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.swt.widgets.Display; import org.xmind.gef.draw2d.IAnchor; import org.xmind.gef.draw2d.decoration.IShadowedDecoration; import org.xmind.gef.draw2d.decoration.PathConnectionDecoration; import org.xmind.gef.draw2d.geometry.PrecisionLine; import org.xmind.gef.draw2d.geometry.PrecisionLine.LineType; import org.xmind.gef.draw2d.geometry.PrecisionPoint; import org.xmind.gef.draw2d.graphics.Path; import org.xmind.ui.internal.figures.TopicFigure; public abstract class AbstractBranchConnection extends PathConnectionDecoration implements IBranchConnectionDecoration, IShadowedDecoration { private int sourceOrientation = PositionConstants.NONE; private int targetOrientation = PositionConstants.NONE; private int sourceExpansion = 0; private int targetExpansion = 0; private boolean tapered = false; private boolean taperedStateChanged = false; protected AbstractBranchConnection() { super(); } protected AbstractBranchConnection(String id) { super(id); } 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 reroute(IFigure figure, PrecisionPoint sourcePos, PrecisionPoint targetPos, boolean validating) { calculateTerminalPoints(figure, sourcePos, targetPos); calculateControlPoints(figure, sourcePos, targetPos); } protected void calculateControlPoints(IFigure figure, PrecisionPoint sourcePos, PrecisionPoint targetPos) { } protected void calculateTerminalPoints(IFigure figure, PrecisionPoint sourcePos, PrecisionPoint targetPos) { IAnchor sa = getSourceAnchor(); IAnchor ta = getTargetAnchor(); int so = getSourceOrientation(); int to = getTargetOrientation(); if (so == PositionConstants.NONE && to == PositionConstants.NONE) { if (sa != null) { if (ta != null) { sourcePos.setLocation(sa.getLocation(ta.getReferencePoint(), getSourceExpansion())); } else { sourcePos.setLocation( sa.getLocation(0, 0, getSourceExpansion())); } } if (ta != null) { if (sa != null) { targetPos.setLocation(ta.getLocation(sa.getReferencePoint(), getTargetExpansion())); } else { targetPos.setLocation( ta.getLocation(0, 0, getTargetExpansion())); } } } else if (so == PositionConstants.NONE) { if (ta != null) { targetPos.setLocation(ta.getLocation(to, getTargetExpansion())); } if (sa != null) { sourcePos.setLocation( sa.getLocation(targetPos, getSourceExpansion())); // sourcePos.setLocation(sa.getOwner().getBounds().getCenter()); } } else if (to == PositionConstants.NONE) { if (sa != null) { sourcePos.setLocation(sa.getLocation(so, getSourceExpansion())); } if (ta != null) { targetPos.setLocation( ta.getLocation(sourcePos, getTargetExpansion())); } } else { if (sa != null) { sourcePos.setLocation(sa.getLocation(so, getSourceExpansion())); } if (ta != null) { targetPos.setLocation(ta.getLocation(to, getTargetExpansion())); } } } protected void calculateSourceControlPoints(PrecisionPoint c, PrecisionPoint s1, PrecisionPoint s2, PrecisionPoint c2) { PrecisionLine line = new PrecisionLine(c, s1, LineType.Ray); double length = s1.getDistance(s2); double angle = line.getAngle(); double sin = Math.sin(angle); double cos = Math.cos(angle); c2.setLocation( s1.getTranslated(length * cos * 0.5f, length * sin * 0.5f)); } public int getSourceOrientation() { return sourceOrientation; } public int getTargetOrientation() { return targetOrientation; } public void setSourceOrientation(IFigure figure, int orientation) { if (orientation == this.sourceOrientation) return; this.sourceOrientation = orientation; if (figure != null) { figure.revalidate(); repaint(figure); } invalidate(); } public void setTargetOrientation(IFigure figure, int orientation) { if (orientation == this.targetOrientation) return; this.targetOrientation = orientation; if (figure != null) { figure.revalidate(); repaint(figure); } invalidate(); } public int getSourceExpansion() { return sourceExpansion; } public int getTargetExpansion() { return targetExpansion; } public void setSourceExpansion(IFigure figure, int expansion) { if (expansion == this.sourceExpansion) return; this.sourceExpansion = expansion; if (figure != null) { figure.revalidate(); repaint(figure); } invalidate(); } public void setTargetExpansion(IFigure figure, int expansion) { if (expansion == this.targetExpansion) return; this.targetExpansion = expansion; if (figure != null) { figure.revalidate(); repaint(figure); } invalidate(); } public boolean isTapered() { return tapered; } public void setTapered(IFigure figure, boolean tapered) { if (tapered == this.tapered) return; this.tapered = tapered; if (figure != null) { repaint(figure); } taperedStateChanged = true; invalidate(); } protected boolean isPositionValid() { return super.isPositionValid() && !taperedStateChanged; } public void validate(IFigure figure) { taperedStateChanged = false; super.validate(figure); } protected boolean usesFill() { return isTapered(); } protected double getThickLineWidth() { return getLineWidth() * 5; } protected void calcTaperedPositions(PrecisionPoint p1, PrecisionPoint p2, double amountFromP1ToP2, PrecisionPoint result1, PrecisionPoint result2) { calcTaperedPositions(p1, p2, amountFromP1ToP2, 5, result1, result2); } protected void calcTaperedPositions(PrecisionPoint p1, PrecisionPoint p2, double amountFromP1ToP2, double thickness, PrecisionPoint result1, PrecisionPoint result2) { // Initialize results result1.setLocation(p1); result2.setLocation(p2); // No more action needed if p1 and p2 are the same location if (p1.equals(p2)) return; // Move results to the point by the amount between p1 and p2 result1.move(result2, amountFromP1ToP2); result2.setLocation(result1); // Calculate the line width on that point double width = getLineWidth() * thickness * (1 - amountFromP1ToP2) + getLineWidth() * amountFromP1ToP2; // Calculate the horizontal and vertical distance from that // point to actual positions double dx = p2.x - p1.x; double dy = p2.y - p1.y; double d = Math.hypot(dx, dy); double e = (width / 2) / d; dx *= e; dy *= e; // Translate results to actual position result1.translate(-dy, dx); result2.translate(dy, -dx); } // public int getMinimumMajorSpacing(IFigure figure) { // return 0; // } protected void paintPath(IFigure figure, Graphics graphics, Path path, boolean fill) { TopicFigure tf = getTopicFigure(figure); if (tf != null && (tf.getDecoration().getLineWidth() > 0 || tf.getDecoration().getFillColor() != null)) { Rectangle bounds = figure.getBounds(); Path shape = new Path(Display.getCurrent()); ITopicDecoration decoration = tf.getDecoration(); Rectangle rectangleClip = graphics.getClip(new Rectangle()); //When get clip from the graphics, there will be a conversion from float to int, with lossing of accuracy. //So expand 1 pixel first. rectangleClip.expand(new Insets(1)); shape.addRectangle(new Rectangle(bounds).intersect(rectangleClip)); shape.addPath(decoration.createClippingPath(tf)); graphics.pushState(); try { graphics.setClip(shape); super.paintPath(figure, graphics, path, fill); graphics.restoreState(); } finally { graphics.popState(); shape.close(); shape.dispose(); } return; } super.paintPath(figure, graphics, path, fill); }; private TopicFigure getTopicFigure(IFigure parent) { for (Object obj : parent.getChildren()) { if (obj instanceof TopicFigure) return (TopicFigure) obj; } return null; } }