/*
* @(#)ConnectorHandle.java
*
* Copyright (c) 1996-2010 The authors and contributors of JHotDraw.
* You may not use, copy or modify this file, except in compliance with the
* accompanying license terms.
*/
package org.jhotdraw.draw.handle;
import javax.annotation.Nullable;
import org.jhotdraw.draw.*;
import org.jhotdraw.draw.connector.Connector;
import org.jhotdraw.draw.ConnectionFigure;
import java.util.*;
import javax.swing.undo.*;
import org.jhotdraw.util.*;
import java.awt.*;
import java.awt.geom.*;
/**
* A {@link Handle} associated to a {@link Connector} which allows to create a new
* {@link ConnectionFigure} by dragging the handle to another connector.
*
* @author Werner Randelshofer.
* @version $Id$
*/
public class ConnectorHandle extends AbstractHandle {
/**
* Holds the ConnectionFigure which is currently being created.
*/
@Nullable
private ConnectionFigure createdConnection;
/**
* The prototype for the ConnectionFigure to be created
*/
private ConnectionFigure prototype;
/**
* The Connector.
*/
private Connector connector;
/**
* The current connectable Figure.
*/
@Nullable
private Figure connectableFigure;
/**
* The current connectable Connector.
*/
@Nullable
private Connector connectableConnector;
/**
* All connectors of the connectable Figure.
*/
protected Collection<Connector> connectors = Collections.emptyList();
/**
* Creates a new instance.
*/
public ConnectorHandle(Connector connector, ConnectionFigure prototype) {
super(connector.getOwner());
this.connector = connector;
this.prototype = prototype;
}
public Point2D.Double getLocationOnDrawing() {
return connector.getAnchor();
}
public Point getLocation() {
return view.drawingToView(connector.getAnchor());
}
@Override
public void draw(Graphics2D g) {
Graphics2D gg = (Graphics2D) g.create();
gg.transform(view.getDrawingToViewTransform());
for (Connector c : connectors) {
c.draw(gg);
}
if (createdConnection == null) {
drawCircle(g,
getEditor().getHandleAttribute(HandleAttributeKeys.DISCONNECTED_CONNECTOR_HANDLE_FILL_COLOR),
getEditor().getHandleAttribute(HandleAttributeKeys.DISCONNECTED_CONNECTOR_HANDLE_STROKE_COLOR));
} else {
drawCircle(g,
getEditor().getHandleAttribute(HandleAttributeKeys.CONNECTED_CONNECTOR_HANDLE_FILL_COLOR),
getEditor().getHandleAttribute(HandleAttributeKeys.CONNECTED_CONNECTOR_HANDLE_STROKE_COLOR));
Point p = view.drawingToView(createdConnection.getEndPoint());
g.setColor(getEditor().getHandleAttribute(HandleAttributeKeys.CONNECTED_CONNECTOR_HANDLE_FILL_COLOR));
int width = getHandlesize();
g.fillOval(p.x - width / 2, p.y - width / 2, width, width);
g.setColor(getEditor().getHandleAttribute(HandleAttributeKeys.CONNECTED_CONNECTOR_HANDLE_STROKE_COLOR));
g.drawOval(p.x - width / 2, p.y - width / 2, width, width);
}
}
@Override
public void trackStart(Point anchor, int modifiersEx) {
setConnection(createConnection());
Point2D.Double p = getLocationOnDrawing();
getConnection().setStartPoint(p);
getConnection().setEndPoint(p);
view.getDrawing().add(getConnection());
}
@Override
public void trackStep(Point anchor, Point lead, int modifiersEx) {
//updateConnectors(lead);
Point2D.Double p = view.viewToDrawing(lead);
fireAreaInvalidated(getDrawingArea());
Figure figure = findConnectableFigure(p, view.getDrawing());
if (figure != connectableFigure) {
connectableFigure = figure;
repaintConnectors();
}
connectableConnector = findConnectableConnector(figure, p);
if (connectableConnector != null) {
p = connectableConnector.getAnchor();
}
getConnection().willChange();
getConnection().setEndPoint(p);
getConnection().changed();
fireAreaInvalidated(getDrawingArea());
}
@Override
public Rectangle getDrawingArea() {
if (getConnection() != null) {
Rectangle r = new Rectangle(
view.drawingToView(getConnection().getEndPoint()));
r.grow(getHandlesize(), getHandlesize());
return r;
} else {
return new Rectangle(); // empty rectangle
}
}
@Override
public void trackEnd(Point anchor, Point lead, int modifiersEx) {
Point2D.Double p = view.viewToDrawing(lead);
if (view.getConstrainer() != null) {
p = view.getConstrainer().constrainPoint(p);
}
Figure f = findConnectableFigure(p, view.getDrawing());
connectableConnector = findConnectableConnector(f, p);
if (connectableConnector != null) {
final Drawing drawing = view.getDrawing();
final ConnectionFigure c = getConnection();
getConnection().setStartConnector(connector);
getConnection().setEndConnector(connectableConnector);
getConnection().updateConnection();
view.clearSelection();
view.addToSelection(c);
view.getDrawing().fireUndoableEditHappened(new AbstractUndoableEdit() {
private static final long serialVersionUID = 1L;
@Override
public String getPresentationName() {
ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels");
return labels.getString("edit.createConnectionFigure.text");
}
@Override
public void undo() throws CannotUndoException {
super.undo();
drawing.remove(c);
}
@Override
public void redo() throws CannotRedoException {
super.redo();
drawing.add(c);
view.clearSelection();
view.addToSelection(c);
}
});
} else {
view.getDrawing().remove(getConnection());
fireAreaInvalidated(getDrawingArea());
}
connectableConnector = null;
connectors = Collections.emptyList();
setConnection(null);
setTargetFigure(null);
}
/**
* Creates the ConnectionFigure. By default the figure prototype is cloned.
*/
protected ConnectionFigure createConnection() {
return (ConnectionFigure) prototype.clone();
}
protected void setConnection(@Nullable ConnectionFigure newConnection) {
createdConnection = newConnection;
}
@Nullable
protected ConnectionFigure getConnection() {
return createdConnection;
}
@Nullable
protected Figure getTargetFigure() {
return connectableFigure;
}
protected void setTargetFigure(@Nullable Figure newTargetFigure) {
connectableFigure = newTargetFigure;
}
@Nullable
private Figure findConnectableFigure(Point2D.Double p, Drawing drawing) {
for (Figure figure : drawing.getFiguresFrontToBack()) {
if (!figure.includes(getConnection())
&& figure.isConnectable()
&& figure.contains(p)) {
return figure;
}
}
return null;
}
/**
* Finds a connection end figure.
*/
@Nullable
protected Connector findConnectableConnector(Figure connectableFigure, Point2D.Double p) {
Connector target = (connectableFigure == null) ? null : connectableFigure.findConnector(p, getConnection());
if ((connectableFigure != null) && connectableFigure.isConnectable() && !connectableFigure.includes(getOwner()) && getConnection().canConnect(connector, target)) {
return target;
}
return null;
}
@Override
protected Rectangle basicGetBounds() {
Rectangle r = new Rectangle(getLocation());
int h = getHandlesize();
r.x -= h / 2;
r.y -= h / 2;
r.width = r.height = h;
return r;
}
@Override
public boolean isCombinableWith(Handle handle) {
return false;
}
/**
* Updates the list of connectors that we draw when the user moves or drags the mouse over a
* figure to which can connect.
*/
public void repaintConnectors() {
Rectangle2D.Double invalidArea = null;
for (Connector c : connectors) {
if (invalidArea == null) {
invalidArea = c.getDrawingArea();
} else {
invalidArea.add(c.getDrawingArea());
}
}
connectors = (connectableFigure == null) ? new java.util.LinkedList<>() : connectableFigure.getConnectors(prototype);
for (Connector c : connectors) {
if (invalidArea == null) {
invalidArea = c.getDrawingArea();
} else {
invalidArea.add(c.getDrawingArea());
}
}
if (invalidArea != null) {
view.getComponent().repaint(
view.drawingToView(invalidArea));
}
}
}