/*******************************************************************************
* Copyright (c) 2006-2012
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
/*
* @(#)BidirectionalConnectionTool.java 2.1 2007-05-18
*
* Copyright (c) 1996-2007 by the original authors of JHotDraw
* and all its contributors.
* All rights reserved.
*
* The copyright of this software is owned by the authors and
* contributors of the JHotDraw project ("the copyright holders").
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* the copyright holders. For details see accompanying license terms.
*/
package org.jhotdraw.draw;
import org.jhotdraw.util.*;
import org.jhotdraw.undo.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;
import java.awt.dnd.*;
/**
* 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.
* <p>
* FIXME: Use a Tracker instance for each state of this tool.
*
* @author Werner Randelshofer
* @version 2.1 2007-05-18 Changed due to changes in the canConnect methods
* of the ConnectionFigure interface.
* <br>2.0 2006-01-14 Changed to support double precision coordinates.
* <br>1.0 2003-12-01 Derived from JHotDraw 5.4b1.
*/
public class BidirectionalConnectionTool extends AbstractTool implements FigureListener {
private Map<AttributeKey, Object> attributes;
/**
* the anchor point of the interaction
*/
private Connector startConnector;
private Connector endConnector;
private Connector targetConnector;
private Figure target;
/**
* the currently created figure
*/
private ConnectionFigure connection;
/**
* the currently manipulated connection point
*/
private int splitPoint;
/**
* the currently edited connection
*/
private ConnectionFigure editedConnection;
/**
* the figure that was actually added
* Note, this can be a different figure from the one which has been created.
*/
private Figure createdFigure;
/**
* the prototypical figure that is used to create new
* connections.
*/
private ConnectionFigure prototype;
/** Creates a new instance. */
public BidirectionalConnectionTool(ConnectionFigure prototype) {
this.prototype = prototype;
}
public BidirectionalConnectionTool(ConnectionFigure prototype, Map<AttributeKey, Object> attributes) {
this.prototype = prototype;
this.attributes = attributes;
}
public void mouseMoved(MouseEvent evt) {
trackConnectors(evt);
}
/**
* Manipulates connections in a context dependent way. If the
* mouse down hits a figure start a new connection. If the mousedown
* hits a connection split a segment or join two segments.
*/
public void mousePressed(MouseEvent evt) {
super.mousePressed(evt);
Point2D.Double ap = viewToDrawing(anchor);
setTargetFigure(findConnectionStart(ap, getDrawing()));
if (getTargetFigure() != null) {
setStartConnector(findConnector(ap, target, prototype));
if (getStartConnector() != null && prototype.canConnect(getTargetConnector())) {
Point2D.Double p = getStartConnector().getAnchor();
setConnection(createFigure());
ConnectionFigure cf = getConnection();
cf.setBounds(p, p);
cf.addFigureListener(this);
setCreatedFigure(cf);
}
}
}
/**
* Adjust the created connection or split segment.
*/
public void mouseDragged(java.awt.event.MouseEvent e) {
Point2D.Double p = viewToDrawing(new Point(e.getX(), e.getY()));
if (getConnection() != null) {
trackConnectors(e);
if (getTargetConnector() != null) {
p = getTargetConnector().getAnchor();
}
getConnection().setEndPoint(p);
} else if (editedConnection != null) {
editedConnection.setPoint(splitPoint, p);
}
}
/**
* Connects the figures if the mouse is released over another
* figure.
*/
public void mouseReleased(MouseEvent e) {
Figure c = null;
Point2D.Double p = viewToDrawing(new Point(e.getX(), e.getY()));
if (getStartConnector() != null) {
c = findTarget(p, getDrawing());
}
if (c != null) {
setEndConnector(findConnector(p, c, prototype));
if (getEndConnector() != null) {
CompositeEdit creationEdit = new CompositeEdit("Verbindung erstellen");
getDrawing().fireUndoableEditHappened(creationEdit);
getDrawing().add(getConnection());
if (getConnection().canConnect(getStartConnector(), getEndConnector())) {
getConnection().setStartConnector(getStartConnector());
getConnection().setEndConnector(getEndConnector());
} else {
getConnection().setStartConnector(getEndConnector());
getConnection().setEndConnector(getStartConnector());
}
getConnection().updateConnection();
getConnection().removeFigureListener(this);
getDrawing().fireUndoableEditHappened(creationEdit);
}
} else if (getConnection() != null) {
getDrawing().remove(getConnection());
}
setConnection(null);
setStartConnector(null);
setEndConnector(null);
setCreatedFigure(null);
fireToolDone();
}
@Override
public void activate(DrawingEditor editor) {
super.activate(editor);
getView().clearSelection();
}
@Override
public void deactivate(DrawingEditor editor) {
super.deactivate(editor);
}
//--
/**
* Creates the ConnectionFigure. By default the figure prototype is
* cloned.
*/
@SuppressWarnings("unchecked")
protected ConnectionFigure createFigure() {
ConnectionFigure f = (ConnectionFigure) prototype.clone();
getEditor().applyDefaultAttributesTo(f);
if (attributes != null) {
for (Map.Entry<AttributeKey, Object> entry : attributes.entrySet()) {
entry.getKey().basicSet(f, entry.getValue());
}
}
return f;
}
/**
* Finds a connectable figure target.
*/
protected Figure findSource(Point2D.Double p, Drawing drawing) {
return findConnectableFigure(p, drawing);
}
/**
* Finds a connectable figure target.
*/
protected Figure findTarget(Point2D.Double p, Drawing drawing) {
Figure target = findConnectableFigure(p, drawing);
Connector targetConnector = (target == null) ? null : target.findConnector(p, prototype);
Connector startConnector = getStartConnector();
if (targetConnector != null
&& getConnection() != null
&& target.canConnect()
&& (getConnection().canConnect(startConnector, targetConnector) ||
getConnection().canConnect(targetConnector, startConnector))
) {
return target;
}
return null;
}
/**
* Finds an existing connection figure.
*/
protected ConnectionFigure findConnection(Point2D.Double p, Drawing drawing) {
for (Figure f : drawing.getFiguresFrontToBack()) {
Figure fInside = f.findFigureInside(p);
if (fInside != null && (fInside instanceof ConnectionFigure)) {
return (ConnectionFigure) fInside;
}
}
return null;
}
private void setConnection(ConnectionFigure newConnection) {
connection = newConnection;
}
/**
* Gets the connection which is created by this tool
*/
protected ConnectionFigure getConnection() {
return connection;
}
protected void trackConnectors(MouseEvent e) {
Point2D.Double p = viewToDrawing(new Point(e.getX(), e.getY()));
Figure c = null;
if (getStartConnector() == null) {
c = findSource(p, getDrawing());
}
else {
c = findTarget(p, getDrawing());
}
Connector cc = null;
if (c != null) {
cc = findConnector(p, c, prototype);
}
if (cc != getTargetConnector()) {
setTargetConnector(cc);
}
// track the figure containing the mouse
if (c != getTargetFigure()) {
setTargetFigure(c);
Connector targetConnector = getTargetConnector();
}
}
public void draw(Graphics2D g) {
if (createdFigure != null) {
createdFigure.draw(g);
}
}
private Connector findConnector(Point2D.Double p, Figure target, ConnectionFigure f) {
return target.findConnector(p, f);
}
/**
* Finds a connection start figure.
*/
protected Figure findConnectionStart(Point2D.Double p, Drawing drawing) {
Figure target = findConnectableFigure(p, drawing);
if ((target != null) && target.canConnect()) {
return target;
}
return null;
}
private Figure findConnectableFigure(Point2D.Double p, Drawing drawing) {
return drawing.findFigureExcept(p, createdFigure);
}
private void setStartConnector(Connector newStartConnector) {
startConnector = newStartConnector;
}
protected Connector getStartConnector() {
return startConnector;
}
private void setEndConnector(Connector newEndConnector) {
endConnector = newEndConnector;
}
protected Connector getEndConnector() {
return endConnector;
}
private void setTargetConnector(Connector newTargetConnector) {
targetConnector = newTargetConnector;
}
protected Connector getTargetConnector() {
return targetConnector;
}
private void setTargetFigure(Figure newTarget) {
target = newTarget;
}
protected Figure getTargetFigure() {
return target;
}
/**
* Gets the figure that was actually added
* Note, this can be a different figure from the one which has been created.
*/
protected Figure getCreatedFigure() {
return createdFigure;
}
private void setCreatedFigure(Figure newCreatedFigure) {
createdFigure = newCreatedFigure;
}
public void areaInvalidated(FigureEvent evt) {
fireAreaInvalidated(evt.getInvalidatedArea());
}
public void figureAdded(FigureEvent e) {
}
public void figureChanged(FigureEvent e) {
}
public void figureRemoved(FigureEvent e) {
}
public void figureRequestRemove(FigureEvent e) {
}
public void attributeChanged(FigureEvent e) {
}
public void figureHandlesChanged(FigureEvent e) {
}
}