/******************************************************************************* * 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 ******************************************************************************/ /* * @(#)AbstractConnectionHandle.java 3.0 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 java.awt.event.InputEvent; import org.jhotdraw.geom.*; import org.jhotdraw.util.*; import java.awt.*; import java.awt.geom.*; import java.util.*; /** * AbstractConnectionHandle factors the common code for handles * that can be used to change the connection of a ConnectionFigure. * * XXX - Undo/Redo is not implemented yet. * * @author Werner Randelshofer * @version 3.0 2007-05-18 Changed due to changes in the canConnect methods * of the ConnectionFigure interface. Shortened the name from * AbstractChangeConnectionHandle to AbstractConnectionHandle. * <br>2.1 2006-02-16 Remove savedLiner from connection while tracking. * <br>2.0 2006-01-14 Changed to support double coordinates. * <br>1.0 2003-12-01 Derived from JHotDraw 5.4b1. */ public abstract class AbstractConnectionHandle extends AbstractHandle { private Connector savedTarget; private Connector connectableConnector; private Figure connectableFigure; private Point start; /** * We temporarily remove the Liner from the connection figure, while the * handle is being moved. * We store the Liner here, and add it back when the user has finished * the interaction. */ private Liner savedLiner; /** * All connectors of the connectable Figure. */ protected Collection<Connector> connectors = Collections.emptyList(); /** * Initializes the change connection handle. */ protected AbstractConnectionHandle(ConnectionFigure owner) { super(owner); } public ConnectionFigure getOwner() { return (ConnectionFigure) super.getOwner(); } public boolean isCombinableWith(Handle handle) { return false; } /** * Returns the connector of the change. */ protected abstract Connector getTarget(); /** * 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 connectableConnector point. */ protected abstract void setLocation(Point2D.Double p); /** * Returns the start point of the connection. */ protected abstract Point2D.Double getLocation(); /** * Gets the side of the connection that is unaffected by * the change. */ protected Connector getSource() { if (getTarget() == getOwner().getStartConnector()) { return getOwner().getEndConnector(); } return getOwner().getStartConnector(); } /** * Disconnects the connection. */ public void trackStart(Point anchor, int modifiersEx) { savedTarget = getTarget(); start = anchor; savedLiner = getOwner().getLiner(); getOwner().setLiner(null); //disconnect(); fireHandleRequestSecondaryHandles(); } /** * Finds a new connectableConnector of the connection. */ public void trackStep(Point anchor, Point lead, int modifiersEx) { Point2D.Double p = view.viewToDrawing(lead); view.getConstrainer().constrainPoint(p); connectableFigure = findConnectableFigure(p, view.getDrawing()); if (connectableFigure != null) { Connector aTarget = findConnectionTarget(p, view.getDrawing()); if (aTarget != null) { p = aTarget.getAnchor(); } } getOwner().willChange(); setLocation(p); getOwner().changed(); repaintConnectors(); } /** * Connects the figure to the new connectableConnector. If there is no * new connectableConnector the connection reverts to its original one. */ public void trackEnd(Point anchor, Point lead, int modifiersEx) { ConnectionFigure f = getOwner(); // Change node type if ((modifiersEx & (InputEvent.META_DOWN_MASK | InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)) != 0 && (modifiersEx & InputEvent.BUTTON2_DOWN_MASK) == 0) { f.willChange(); int index = getBezierNodeIndex(); BezierPath.Node v = f.getNode(index); if (index > 0 && index < f.getNodeCount()) { v.mask = (v.mask + 3) % 4; } else if (index == 0) { v.mask = ((v.mask & BezierPath.C2_MASK) == 0) ? BezierPath.C2_MASK : 0; } else { v.mask = ((v.mask & BezierPath.C1_MASK) == 0) ? BezierPath.C1_MASK : 0; } f.setNode(index, v); f.changed(); fireHandleRequestSecondaryHandles(); } Point2D.Double p = view.viewToDrawing(lead); view.getConstrainer().constrainPoint(p); Connector target = findConnectionTarget(p, view.getDrawing()); if (target == null) { target = savedTarget; } setLocation(p); if (target != savedTarget) { disconnect(); connect(target); } getOwner().setLiner(savedLiner); getOwner().updateConnection(); connectableConnector = null; connectors = Collections.emptyList(); } private Connector findConnectionTarget(Point2D.Double p, Drawing drawing) { Figure targetFigure = findConnectableFigure(p, drawing); if (getSource() == null && targetFigure != null) { return findConnector(p, targetFigure, getOwner()); } else if (targetFigure != null) { Connector target = findConnector(p, targetFigure, getOwner()); if ((targetFigure != null) && targetFigure.canConnect()// && targetFigure != savedTarget // && !targetFigure.includes(getOwner()) // && (canConnect(getSource(), target))) { return target; } } return null; } protected abstract boolean canConnect(Connector existingEnd, Connector targetEnd); protected Connector findConnector(Point2D.Double p, Figure f, ConnectionFigure prototype) { return f.findConnector(p, prototype); } /** * Draws this handle. */ @Override public void draw(Graphics2D g) { Graphics2D gg = (Graphics2D) g.create(); gg.transform(view.getDrawingToViewTransform()); for (Connector c : connectors) { c.draw(gg); } gg.dispose(); if (getTarget() == null) { drawCircle(g, (Color) getEditor().getHandleAttribute(HandleAttributeKeys.DISCONNECTED_CONNECTION_HANDLE_FILL_COLOR), (Color) getEditor().getHandleAttribute(HandleAttributeKeys.DISCONNECTED_CONNECTION_HANDLE_STROKE_COLOR)); } else { drawCircle(g, (Color) getEditor().getHandleAttribute(HandleAttributeKeys.CONNECTED_CONNECTION_HANDLE_FILL_COLOR), (Color) getEditor().getHandleAttribute(HandleAttributeKeys.CONNECTED_CONNECTION_HANDLE_STROKE_COLOR)); } } private Figure findConnectableFigure(Point2D.Double p, Drawing drawing) { for (Figure f : drawing.getFiguresFrontToBack()) { if (!f.includes(getOwner()) && f.canConnect() && f.contains(p)) { return f; } } return null; } protected void setPotentialTarget(Connector newTarget) { this.connectableConnector = newTarget; } protected Rectangle basicGetBounds() { //if (connection.getPointCount() == 0) return new Rectangle(0, 0, getHandlesize(), getHandlesize()); Point center = view.drawingToView(getLocation()); return new Rectangle(center.x - getHandlesize() / 2, center.y - getHandlesize() / 2, getHandlesize(), getHandlesize()); } protected BezierFigure getBezierFigure() { return (BezierFigure) getOwner(); } abstract protected int getBezierNodeIndex(); @Override final public Collection<Handle> createSecondaryHandles() { LinkedList<Handle> list = new LinkedList<Handle>(); if (((ConnectionFigure) getOwner()).getLiner() == null && savedLiner == null) { int index = getBezierNodeIndex(); BezierFigure f = getBezierFigure(); BezierPath.Node v = f.getNode(index); if ((v.mask & BezierPath.C1_MASK) != 0 && (index != 0 || f.isClosed())) { list.add(new BezierControlPointHandle(f, index, 1)); } if ((v.mask & BezierPath.C2_MASK) != 0 && (index < f.getNodeCount() - 1 || f.isClosed())) { list.add(new BezierControlPointHandle(f, index, 2)); } if (index > 0 || f.isClosed()) { int i = (index == 0) ? f.getNodeCount() - 1 : index - 1; v = f.getNode(i); if ((v.mask & BezierPath.C2_MASK) != 0) { list.add(new BezierControlPointHandle(f, i, 2)); } } if (index < f.getNodeCount() - 2 || f.isClosed()) { int i = (index == f.getNodeCount() - 1) ? 0 : index + 1; v = f.getNode(i); if ((v.mask & BezierPath.C1_MASK) != 0) { list.add(new BezierControlPointHandle(f, i, 1)); } } } return list; } protected BezierPath.Node getBezierNode() { int index = getBezierNodeIndex(); return getBezierFigure().getNodeCount() > index ? getBezierFigure().getNode(index) : null; } public String getToolTipText(Point p) { ConnectionFigure f = (ConnectionFigure) getOwner(); if (f.getLiner() == null && savedLiner == null) { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); BezierPath.Node node = getBezierNode(); return (node == null) ? null : labels.getFormatted("handle.bezierNode.toolTipText", labels.getFormatted( (node.getMask() == 0) ? "bezierNode.linearNode" : ((node.getMask() == BezierPath.C1C2_MASK) ? "bezierNode.cubicNode" : "bezierNode.quadraticNode"))); } else { return null; } } /** * 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<Connector>() : connectableFigure.getConnectors(getOwner()); for (Connector c : connectors) { if (invalidArea == null) { invalidArea = c.getDrawingArea(); } else { invalidArea.add(c.getDrawingArea()); } } if (invalidArea != null) { view.getComponent().repaint( view.drawingToView(invalidArea)); } } }