ChangeConnectionHandle.java
/*
* @(#)ChangeConnectionHandle.java
*
* Project: JHotdraw - a GUI framework for technical drawings
* http://www.jhotdraw.org
* http://jhotdraw.sourceforge.net
* Copyright: © by the original author(s) and all contributors
* License: Lesser GNU Public License (LGPL)
* http://www.opensource.org/licenses/lgpl-license.html
*/
package CH.ifa.draw.standard;
import CH.ifa.draw.framework.*;
import CH.ifa.draw.util.Geom;
import CH.ifa.draw.util.Undoable;
import CH.ifa.draw.util.UndoableAdapter;
import java.awt.*;
/**
* ChangeConnectionHandle factors the common code for handles
* that can be used to reconnect connections.
*
* @see ChangeConnectionEndHandle
* @see ChangeConnectionStartHandle
*
* @version <$CURRENT_VERSION$>
*/
public abstract class ChangeConnectionHandle extends AbstractHandle {
private Connector fOriginalTarget;
private Figure myTarget;
private ConnectionFigure myConnection;
private Point fStart;
/**
* Initializes the change connection handle.
*/
protected ChangeConnectionHandle(Figure owner) {
super(owner);
setConnection((ConnectionFigure) owner());
setTargetFigure(null);
}
/**
* Returns the target connector of the change.
*/
protected abstract Connector target();
/**
* Disconnects the connection.
*/
protected abstract void disconnect();
/**
* Connect the connection with the given figure.
*/
protected abstract void connect(Connector c);
/**
* Sets the location of the target point.
*/
protected abstract void setPoint(int x, int y);
/**
* Gets the side of the connection that is unaffected by
* the change.
*/
protected Connector source() {
if (target() == getConnection().getStartConnector()) {
return getConnection().getEndConnector();
}
return getConnection().getStartConnector();
}
/**
* Disconnects the connection.
*/
public void invokeStart(int x, int y, DrawingView view) {
fOriginalTarget = target();
fStart = new Point(x, y);
setUndoActivity(createUndoActivity(view));
((ChangeConnectionHandle.UndoActivity)getUndoActivity()).setOldConnector(target());
disconnect();
}
/**
* Finds a new target of the connection.
*/
public void invokeStep (int x, int y, int anchorX, int anchorY, DrawingView view) {
Point p = new Point(x, y);
Figure f = findConnectableFigure(x, y, view.drawing());
// track the figure containing the mouse
if (f != getTargetFigure()) {
if (getTargetFigure() != null) {
getTargetFigure().connectorVisibility(false);
}
setTargetFigure(f);
if (getTargetFigure() != null) {
getTargetFigure().connectorVisibility(true);
}
}
Connector target = findConnectionTarget(p.x, p.y, view.drawing());
if (target != null) {
p = Geom.center(target.displayBox());
}
setPoint(p.x, p.y);
}
/**
* Connects the figure to the new target. If there is no
* new target the connection reverts to its original one.
*/
public void invokeEnd(int x, int y, int anchorX, int anchorY, DrawingView view) {
Connector target = findConnectionTarget(x, y, view.drawing());
if (target == null) {
target = fOriginalTarget;
}
setPoint(x, y);
connect(target);
getConnection().updateConnection();
Connector oldConnector = ((ChangeConnectionHandle.UndoActivity)
getUndoActivity()).getOldConnector();
// there has been no change so there is nothing to undo
if ((oldConnector == null)
|| (target() == null)
|| (oldConnector.owner() == target().owner())) {
setUndoActivity(null);
}
else {
getUndoActivity().setAffectedFigures(new SingleFigureEnumerator(getConnection()));
}
if (getTargetFigure() != null) {
getTargetFigure().connectorVisibility(false);
setTargetFigure(null);
}
}
private Connector findConnectionTarget(int x, int y, Drawing drawing) {
Figure target = findConnectableFigure(x, y, drawing);
if ((target != null) && target.canConnect()
&& target != fOriginalTarget
&& !target.includes(owner())
&& getConnection().canConnect(source().owner(), target)) {
return findConnector(x, y, target);
}
return null;
}
protected Connector findConnector(int x, int y, Figure f) {
return f.connectorAt(x, y);
}
/**
* Draws this handle.
*/
public void draw(Graphics g) {
Rectangle r = displayBox();
g.setColor(Color.green);
g.fillRect(r.x, r.y, r.width, r.height);
g.setColor(Color.black);
g.drawRect(r.x, r.y, r.width, r.height);
}
private Figure findConnectableFigure(int x, int y, Drawing drawing) {
FigureEnumeration k = drawing.figuresReverse();
while (k.hasMoreElements()) {
Figure figure = k.nextFigure();
if (!figure.includes(getConnection()) && figure.canConnect()) {
if (figure.containsPoint(x, y)) {
return figure;
}
}
}
return null;
}
protected void setConnection(ConnectionFigure newConnection) {
myConnection = newConnection;
}
protected ConnectionFigure getConnection() {
return myConnection;
}
protected void setTargetFigure(Figure newTarget) {
myTarget = newTarget;
}
protected Figure getTargetFigure() {
return myTarget;
}
/**
* Factory method for undo activity. To be overriden by subclasses.
*/
protected abstract Undoable createUndoActivity(DrawingView newView);
public static abstract class UndoActivity extends UndoableAdapter {
private Connector myOldConnector;
public UndoActivity(DrawingView newView) {
super(newView);
setUndoable(true);
setRedoable(true);
}
public boolean undo() {
if (!super.undo()) {
return false;
}
swapConnectors();
return true;
}
public boolean redo() {
// do not call execute directly as the selection might has changed
if (!isRedoable()) {
return false;
}
swapConnectors();
return true;
}
private void swapConnectors() {
FigureEnumeration fe = getAffectedFigures();
if (fe.hasMoreElements()) {
ConnectionFigure connection = (ConnectionFigure)fe.nextFigure();
setOldConnector(replaceConnector(connection));
connection.updateConnection();
}
}
protected abstract Connector replaceConnector(ConnectionFigure connection);
public void setOldConnector(Connector newOldConnector) {
myOldConnector = newOldConnector;
}
public Connector getOldConnector() {
return myOldConnector;
}
}
}