/*
* @(#)ConnectionTool.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 research.tool;
import research.util.Geom;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.*;
import research.*;
import research.connector.Connector;
/**
* A tool that can be used to connect figures, to split
* connections, and to join two segments of a connection.
* ConnectionTools turns the visibility of the Connectors
* on when it enters a figure.
* The connection object to be created is specified by a prototype.
* <hr>
* <b>Design Patterns</b><P>
* <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
* <b><a href=../pattlets/sld029.htm>Prototype</a></b><br>
* ConnectionTools creates the connection by cloning a prototype.
* <hr>
*
* @see ConnectionFigure
* @see Object#clone
*
* @version <$CURRENT_VERSION$>
*/
public class ConnectionTool extends CreationTool {
/**
* the anchor point of the interaction
*/
private Connector myStartConnector;
private Connector myEndConnector;
private Connector myTargetConnector;
private Figure myTarget;
/**
* the currently created figure
*/
private ConnectionFigure myConnection;
/**
* the currently manipulated connection point
* Notes: its coordinate is equals with drawing(): added by zhangwei
*/
protected int fSplitPoint;
/**
* the currently edited connection
*/
protected ConnectionFigure fEditedConnection;
/**
* the figure that was actually added
* Note, this can be a different figure from the one which has been created.
*/
private Figure myAddedFigure;
public void mouseEntered(MouseEvent e, int x, int y) {
if (getTargetFigure() != null) {
FigureHelper.setConnectorVisible(getTargetFigure(), true);
}
drawingView.checkDamage();
}
public void mouseExited(MouseEvent e, int x, int y) {
if (getTargetFigure() != null) {
FigureHelper.setConnectorVisible(getTargetFigure(), false);
}
drawingView.checkDamage();
}
/**
* Handles mouse move events in the drawing view.
*/
public void mouseMove(MouseEvent e, int x, int y) {
double scale = drawingView.getScale();
x = (int) (x / scale + 0.5);
y = (int) (y / scale + 0.5);
trackConnectors(e, x, y);
}
public void mouseDown(MouseEvent e, int x, int y) {
double scale = drawingView.getScale();
x = (int) (x / scale + 0.5);
y = (int) (y / scale + 0.5);
setTargetFigure(findConnectionStart(x, y, drawingView.getDrawing()));
if (getTargetFigure() != null) {
setStartConnector(findConnector(x, y, getTargetFigure()));
if (getStartConnector() != null) {
consumeFigure();
setConnection((ConnectionFigure) createdFigure);
getConnection().startPoint(x, y);
getConnection().endPoint(x, y);
setAddedFigure(drawingView.add(getConnection()));
}
} else {
ConnectionFigure connection = findConnection(x, y, drawingView.getDrawing());
if (connection != null) {
if (!connection.joinSegments(x, y)) {
fSplitPoint = connection.splitSegment(x, y);
fEditedConnection = connection;
} else {
fEditedConnection = null;
}
}
}
}
/**
* Adjust the created connection or split segment.
*/
@Override
public void mouseDrag(MouseEvent e, int x, int y) {
double scale = drawingView.getScale();
x = (int) (x / scale + 0.5);
y = (int) (y / scale + 0.5);
Point p = new Point(x, y);
if (getConnection() != null) {
trackConnectors(e, x, y);
if (getTargetConnector() != null) {
Rectangle realR = getTargetConnector().getDisplayBox();
p = Geom.center(realR);
}
getConnection().endPoint(p.x, p.y);
} else if (fEditedConnection != null) {
Point pp = new Point(x, y);
fEditedConnection.setPointAt(pp, fSplitPoint);
}
}
/**
* Connects the figures if the mouse is released over another
* figure.
*/
public void mouseUp(MouseEvent e, int x, int y) {
double scale = drawingView.getScale();
x = (int) (x / scale + 0.5);
y = (int) (y / scale + 0.5);
Figure c = null;
if (getTargetFigure() != null) {
FigureHelper.setConnectorVisible(getTargetFigure(), false);
}
if (getStartConnector() != null) {
c = findTarget(x, y, drawingView.getDrawing());
}
if (c != null) {
setEndConnector(findConnector(x, y, c));
if (getEndConnector() != null) {
getConnection().connectStart(getStartConnector());
getConnection().connectEnd(getEndConnector());
getConnection().updateConnection();
}else if (getConnection() != null) {
drawingView.remove(getConnection());
}
} else if (getConnection() != null) {
drawingView.remove(getConnection());
}
setConnection(null);
setStartConnector(null);
setEndConnector(null);
setAddedFigure(null);
getPropertyChangeSupport().firePropertyChange(Tool.TOOL_DONE, false, true);
}
/**
* Finds a connectable figure target.
*/
/**
protected Figure findSource(int x, int y, Drawing drawing) {
return findConnectableFigure(x, y, drawing);
}
**/
/**
* Finds a connectable figure target.
*/
protected Figure findTarget(int x, int y, Drawing drawing) {
Figure target = findConnectableFigure(x, y, drawing);
Figure start = getStartConnector().owner();
if (target != null
&& getConnection() != null
&& FigureHelper.isConnectable(target)
&& !target.includes(start)
&& getConnection().canConnect(start, target)) {
return target;
}
return null;
}
/**
* Finds an existing connection figure.
*/
protected ConnectionFigure findConnection(int x, int y, Drawing drawing) {
Enumeration k = drawing.getFiguresReverse();
while (k.hasMoreElements()) {
Figure figure = (Figure) k.nextElement();
figure = figure.findFigureInside(x, y);
if (figure != null && (figure instanceof ConnectionFigure)) {
return (ConnectionFigure) figure;
}
}
return null;
}
protected void setConnection(ConnectionFigure newConnection) {
myConnection = newConnection;
}
/**
* Gets the connection which is created by this tool
*/
protected ConnectionFigure getConnection() {
return myConnection;
}
protected void trackConnectors(MouseEvent e, int x, int y) {
Figure c = null;
if (getStartConnector() == null) {
c = findConnectionStart(x, y, drawingView.getDrawing());
} else {
c = findTarget(x, y, drawingView.getDrawing());
}
// track the figure containing the mouse
if (c != getTargetFigure()) {
if (getTargetFigure() != null) {
FigureHelper.setConnectorVisible(getTargetFigure(), false);
}
setTargetFigure(c);
if (getTargetFigure() != null) {
FigureHelper.setConnectorVisible(getTargetFigure(), true);
}
}
Connector cc = null;
if (c != null) {
cc = findConnector(x, y, c);
}
if (cc != getTargetConnector()) {
setTargetConnector(cc);
}
drawingView.checkDamage();
}
protected Connector findConnector(int x, int y, Figure f) {
return f.connectorAt(x, y);
}
/**
* Finds a connection start figure.
*/
protected Figure findConnectionStart(int x, int y, Drawing drawing) {
/**
Figure target = findConnectableFigure(x, y, drawing);
if ((target != null) && FigureHelper.isConnectable(target)) {
return target;
}
return null;
**/
return findConnectableFigure(x, y, drawing);
}
protected Figure findConnectableFigure(int x, int y, Drawing drawing) {
FigureEnumeration k = drawing.getFiguresReverse();
while (k.hasMoreElements()) {
Figure figure = k.nextFigure();
if (!figure.includes(getConnection()) && FigureHelper.isConnectable(figure)
&& figure.containsPoint(x, y)) {
return figure;
}
}
return null;
}
protected void setStartConnector(Connector newStartConnector) {
myStartConnector = newStartConnector;
}
protected Connector getStartConnector() {
return myStartConnector;
}
protected void setEndConnector(Connector newEndConnector) {
myEndConnector = newEndConnector;
}
protected Connector getEndConnector() {
return myEndConnector;
}
protected void setTargetConnector(Connector newTargetConnector) {
myTargetConnector = newTargetConnector;
}
protected Connector getTargetConnector() {
return myTargetConnector;
}
protected void setTargetFigure(Figure newTarget) {
myTarget = newTarget;
}
protected Figure getTargetFigure() {
return myTarget;
}
/**
* Gets the figure that was actually added
* Note, this can be a different figure from the one which has been created.
*/
protected Figure getAddedFigure() {
return myAddedFigure;
}
protected void setAddedFigure(Figure newAddedFigure) {
myAddedFigure = newAddedFigure;
}
/**
* Factory method for undo activity
*/
protected Undoable createUndoActivity() {
return new UndoActivity(drawingView, getConnection());
}
public static class UndoActivity extends UndoableAdapter {
private ConnectionFigure myConnection;
private Connector myStartConnector;
private Connector myEndConnector;
public UndoActivity(DrawingView newDrawingView, ConnectionFigure newConnection) {
super(newDrawingView);
setConnection(newConnection);
myStartConnector = getConnection().getStartConnector();
myEndConnector = getConnection().getEndConnector();
setUndoable(true);
setRedoable(true);
}
/*
* Undo the activity
* @return true if the activity could be undone, false otherwise
*/
public boolean undo() {
if (!super.undo()) {
return false;
}
getConnection().disconnectStart();
getConnection().disconnectEnd();
FigureEnumeration fe = getAffectedFigures();
while (fe.hasMoreElements()) {
getDrawingView().getDrawing().orphan(fe.nextFigure());
}
getDrawingView().clearSelection();
return true;
}
/*
* Redo the activity
* @return true if the activity could be redone, false otherwise
*/
public boolean redo() {
if (!super.redo()) {
return false;
}
getConnection().connectStart(myStartConnector);
getConnection().connectEnd(myEndConnector);
getConnection().updateConnection();
getDrawingView().insertFigures(getAffectedFigures(), 0, 0, false);
return true;
}
private void setConnection(ConnectionFigure newConnection) {
myConnection = newConnection;
}
/**
* Gets the currently created figure
*/
protected ConnectionFigure getConnection() {
return myConnection;
}
}
}