blob: d51c53b6faeb3f1b361b19d5d3a32d44f528b4ac [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.gef.editparts;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.draw2d.ChopboxAnchor;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.draw2d.XYAnchor;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.AccessibleAnchorProvider;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.DragTracker;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.NodeEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.tools.SelectEditPartTracker;
/**
* The base implementation for {@link org.eclipse.gef.ConnectionEditPart}.
*/
public abstract class AbstractConnectionEditPart extends
AbstractGraphicalEditPart implements ConnectionEditPart, LayerConstants {
private static final ConnectionAnchor DEFAULT_SOURCE_ANCHOR = new XYAnchor(
new Point(10, 10));
private static final ConnectionAnchor DEFAULT_TARGET_ANCHOR = new XYAnchor(
new Point(100, 100));
/**
* Provides accessibility support for when connections are also themselves
* nodes. If a connection is the source or target of another connection,
* then its midpoint is used as the accessible anchor location.
*
* @author hudsonr
* @since 2.0
*/
protected final class DefaultAccessibleAnchorProvider implements
AccessibleAnchorProvider {
/**
* This class is internal, but is made protected so that JavaDoc will
* see it.
*/
DefaultAccessibleAnchorProvider() {
}
/**
* @see AccessibleAnchorProvider#getSourceAnchorLocations()
*/
public List getSourceAnchorLocations() {
List list = new ArrayList();
if (getFigure() instanceof Connection) {
Point p = ((Connection) getFigure()).getPoints().getMidpoint();
getFigure().translateToAbsolute(p);
list.add(p);
}
return list;
}
/**
* @see AccessibleAnchorProvider#getTargetAnchorLocations()
*/
public List getTargetAnchorLocations() {
return getSourceAnchorLocations();
}
}
private EditPart sourceEditPart, targetEditPart;
/**
* Activates the Figure representing this, by setting up the start and end
* connections, and adding the figure to the Connection Layer.
*
* @see #deactivate()
*/
protected void activateFigure() {
getLayer(CONNECTION_LAYER).add(getFigure());
}
/**
* @see org.eclipse.gef.EditPart#addNotify()
*/
public void addNotify() {
activateFigure();
super.addNotify();
}
/**
* Returns a newly created Figure to represent these type of EditParts.
*
* @return The created Figure.
*/
protected IFigure createFigure() {
return new PolylineConnection();
}
/**
* Deactivates the Figure representing this, by removing it from the
* connection layer, and resetting the source and target connections to
* <code>null</code>.
*/
protected void deactivateFigure() {
getLayer(CONNECTION_LAYER).remove(getFigure());
getConnectionFigure().setSourceAnchor(null);
getConnectionFigure().setTargetAnchor(null);
}
/**
* <code>AbstractConnectionEditPart</code> extends getAdapter() to overrides
* the {@link AccessibleAnchorProvider} adapter returned by the superclass.
* When treating a connection as a node for other connections, it makes
* sense to target its midpoint, and not the edge of its bounds.
*
* @see AbstractConnectionEditPart.DefaultAccessibleAnchorProvider
* @see AbstractGraphicalEditPart#getAdapter(Class)
* @param adapter
* the adapter Class
* @return the adapter
*/
public Object getAdapter(Class adapter) {
if (adapter == AccessibleAnchorProvider.class)
return new DefaultAccessibleAnchorProvider();
return super.getAdapter(adapter);
}
/**
* Convenience method for casting this GraphicalEditPart's Figure to a
* {@link Connection}
*
* @return the Figure as a Connection
*/
public Connection getConnectionFigure() {
return (Connection) getFigure();
}
/**
* @see org.eclipse.gef.EditPart#getDragTracker(Request)
*/
public DragTracker getDragTracker(Request req) {
return new SelectEditPartTracker(this);
}
/**
* @see org.eclipse.gef.ConnectionEditPart#getSource()
*/
public EditPart getSource() {
return sourceEditPart;
}
/**
* @see org.eclipse.gef.ConnectionEditPart#getTarget()
*/
public EditPart getTarget() {
return targetEditPart;
}
/**
* Returns the <code>ConnectionAnchor</code> for the <i>source</i> end of
* the connection. If the source is an instance of {@link NodeEditPart},
* that interface will be used to determine the proper ConnectionAnchor. If
* the source is not an instance of <code>NodeEditPart</code>, this method
* should be overridden to return the correct ConnectionAnchor. Failure to
* do this will cause a default anchor to be used so that the connection
* figure will be made visible to the developer.
*
* @return ConnectionAnchor for the source end of the Connection
*/
protected ConnectionAnchor getSourceConnectionAnchor() {
if (getSource() != null) {
if (getSource() instanceof NodeEditPart) {
NodeEditPart editPart = (NodeEditPart) getSource();
return editPart.getSourceConnectionAnchor(this);
}
IFigure f = ((GraphicalEditPart) getSource()).getFigure();
return new ChopboxAnchor(f);
}
return DEFAULT_SOURCE_ANCHOR;
}
/**
* Returns the <code>ConnectionAnchor</code> for the <i>target</i> end of
* the connection. If the target is an instance of {@link NodeEditPart},
* that interface will be used to determine the proper ConnectionAnchor. If
* the target is not an instance of <code>NodeEditPart</code>, this method
* should be overridden to return the correct ConnectionAnchor. Failure to
* do this will cause a default anchor to be used so that the connection
* figure will be made visible to the developer.
*
* @return ConnectionAnchor for the target end of the Connection
*/
protected ConnectionAnchor getTargetConnectionAnchor() {
if (getTarget() != null) {
if (getTarget() instanceof NodeEditPart) {
NodeEditPart editPart = (NodeEditPart) getTarget();
return editPart.getTargetConnectionAnchor(this);
}
IFigure f = ((GraphicalEditPart) getTarget()).getFigure();
return new ChopboxAnchor(f);
}
return DEFAULT_TARGET_ANCHOR;
}
/**
* Extended here to also refresh the ConnectionAnchors.
*
* @see org.eclipse.gef.EditPart#refresh()
*/
public void refresh() {
refreshSourceAnchor();
refreshTargetAnchor();
super.refresh();
}
/**
* Updates the source ConnectionAnchor. Subclasses should override
* {@link #getSourceConnectionAnchor()} if necessary, and not this method.
*/
protected void refreshSourceAnchor() {
getConnectionFigure().setSourceAnchor(getSourceConnectionAnchor());
}
/**
* Updates the target ConnectionAnchor. Subclasses should override
* {@link #getTargetConnectionAnchor()} if necessary, and not this method.
*/
protected void refreshTargetAnchor() {
getConnectionFigure().setTargetAnchor(getTargetConnectionAnchor());
}
/**
* Extended here to remove the ConnectionEditPart's connection figure from
* the connection layer.
*
* @see org.eclipse.gef.EditPart#removeNotify()
*/
public void removeNotify() {
deactivateFigure();
super.removeNotify();
}
/**
* Extended to implement automatic addNotify and removeNotify handling.
*
* @see org.eclipse.gef.EditPart#setParent(EditPart)
*/
public void setParent(EditPart parent) {
boolean wasNull = getParent() == null;
boolean becomingNull = parent == null;
if (becomingNull && !wasNull)
removeNotify();
super.setParent(parent);
if (wasNull && !becomingNull)
addNotify();
}
/**
* Sets the source EditPart of this connection.
*
* @param editPart
* EditPart which is the source.
*/
public void setSource(EditPart editPart) {
if (sourceEditPart == editPart)
return;
sourceEditPart = editPart;
if (sourceEditPart != null)
setParent(sourceEditPart.getRoot());
else if (getTarget() == null)
setParent(null);
if (sourceEditPart != null && targetEditPart != null)
refresh();
}
/**
* Sets the target EditPart of this connection.
*
* @param editPart
* EditPart which is the target.
*/
public void setTarget(EditPart editPart) {
if (targetEditPart == editPart)
return;
targetEditPart = editPart;
if (editPart != null)
setParent(editPart.getRoot());
else if (getSource() == null)
setParent(null);
if (sourceEditPart != null && targetEditPart != null)
refresh();
}
}