/*
* @(#)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 research;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
import research.connector.Connector;
import research.tool.Tool;
import research.util.Geom;
import de.FeatureModellingTool.GraphicalEditor.AttributeConnection;
import de.FeatureModellingTool.GraphicalEditor.GraphicalEditor;
import de.FeatureModellingTool.GraphicalEditor.DecomposeConnection;
import de.FeatureModellingTool.GraphicalEditor.ModelValidation;
import de.FeatureModellingTool.GraphicalEditor.PLConnection;
/**
* 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;
private Tool operationTool = null;
private static Color fillColor = new Color(0, 255, 0, 128);
/**
* 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));
((UndoActivity) getUndoActivity()).setOldConnector(target());
disconnect();
}
/**
* Finds a new target of the connection.
*/
public void invokeStep(InputEvent inputEvent, int x, int y, int anchorX, int anchorY, DrawingView view) {
Point p = new Point(x, y);
Figure f = findConnectableFigure(x, y, view.getDrawing());
if ((f != null) && (ConnectionFigure.class.isInstance(owner()))) {
ConnectionFigure cf = (ConnectionFigure) owner();
if (ChangeConnectionEndHandle.class.isInstance(this)) {
if (!cf.canConnect(cf.startFigure(), f) || !modelValidate(cf.startFigure(), fOriginalTarget.owner(), cf, f, false)) {
f = null;
}
}else if (ChangeConnectionStartHandle.class.isInstance(this)){
if (!cf.canConnect(f, cf.endFigure()) || !modelValidate(fOriginalTarget.owner(), cf.endFigure(), cf, f, true)) {
f = null;
}
}
}
// track the figure containing the mouse
if (f != getTargetFigure()) {
if (getTargetFigure() != null) {
FigureHelper.setConnectorVisible(getTargetFigure(), false);
}
setTargetFigure(f);
if (getTargetFigure() != null) {
FigureHelper.setConnectorVisible(getTargetFigure(), true);
}
}
Connector target = findConnectionTarget(p.x, p.y, view.getDrawing());
if (target != null) {
p = Geom.center(target.getDisplayBox());
}
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.getDrawing());
if (target == null) {
target = fOriginalTarget;
}
setPoint(x, y);
connect(target);
//getConnection().updateConnection();
getConnection().layoutConnection();
Connector oldConnector = ((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);
FigureHelper.setConnectorVisible(getTargetFigure(), false);
setTargetFigure(null);
}
}
private Connector findConnectionTarget(int x, int y, Drawing drawing) {
Figure target = findConnectableFigure(x, y, drawing);
if ((target != null)//��ǰͼԪ��Ϊ��
&& FigureHelper.isConnectable(target)//��ǰͼԪ������
&& target != fOriginalTarget
&& !target.includes(owner())){
if(((this instanceof ChangeConnectionStartHandle)&&(getConnection().canConnect(target, source().owner()))
&& modelValidate(fOriginalTarget.owner(), getConnection().endFigure(), getConnection(), target, true))
||((this instanceof ChangeConnectionEndHandle)&&getConnection().canConnect(source().owner(), target)
&& modelValidate(getConnection().startFigure(), fOriginalTarget.owner(), getConnection(), target, false)))
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(fillColor);
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.getFiguresReverse();
while (k.hasMoreElements()) {
Figure figure = k.nextFigure();
if (!figure.includes(getConnection()) && FigureHelper.isConnectable(figure)) {
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;
}
}
public void setOperationTool(Tool operationTool) {
this.operationTool = operationTool;
}
private boolean modelValidate(Figure start, Figure end, ConnectionFigure conn, Figure newNode, boolean bChangeStart) {
if (start == null || end == null ||conn == null || newNode == null)
return false;
ModelValidation modelValidation = (ModelValidation)operationTool.getContext().getValue(GraphicalEditor.MODEL_VALIDATION);
if (modelValidation == null)
return true;
String startId = (String)start.getAttribute("id");//--constraintId
String endId = (String)end.getAttribute("id");//--featureId
String type = (String)conn.getAttribute("type");
String newNodeId = (String)newNode.getAttribute("id");
if (conn instanceof PLConnection) {
Connector connector = conn.getStartConnector();
if (connector == null) { //--change plConnection start
boolean bReturn = modelValidation.canChangeConnection(endId, startId, false, newNodeId, bChangeStart);
if (bReturn)
bReturn = modelValidation.canChangeConnection(endId, startId, true, newNodeId, bChangeStart);
return bReturn;
}
int nDirection = connector.getDirection();
if (nDirection == Connector.EAST_DIRECTION || nDirection == Connector.SOUTH_DIRECTION) //--sink
return modelValidation.canChangeConnection(endId, startId, false, newNodeId, bChangeStart);
else
return modelValidation.canChangeConnection(endId, startId, true, newNodeId, bChangeStart);
}
if (conn instanceof DecomposeConnection || conn instanceof AttributeConnection)
return modelValidation.canChangeConnection(startId, endId, newNodeId, bChangeStart, type);
return true;
}
}