view org.eclipse.draw2d/src/org/eclipse/draw2d/PolylineConnection.d @ 16:dbfb303e8fb0

first complete successful compile (win-only)
author Frank Benoit <benoit@tionex.de>
date Wed, 18 Mar 2009 08:56:47 +0100
parents bc29606a740c
children
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 * Port to the D programming language:
 *     Frank Benoit <benoit@tionex.de>
 *******************************************************************************/
module org.eclipse.draw2d.PolylineConnection;

import java.lang.all;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.Polyline;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.AnchorListener;
import org.eclipse.draw2d.ConnectionRouter;
import org.eclipse.draw2d.ConnectionLocator;
import org.eclipse.draw2d.RotatableDecoration;
import org.eclipse.draw2d.RoutingListener;
import org.eclipse.draw2d.DelegatingLayout;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.ArrowLocator;

/**
 * An implementation of {@link Connection} based on Polyline.  PolylineConnection adds
 * the following additional features:
 * <UL>
 * <LI>
 *   A {@link ConnectionRouter} may be provided which will be used to determine the
 *   connections points.
 * <LI>
 *   Children may be added. The bounds calculation is extended such that the bounds is
 *   the smallest Rectangle which is large enough to display the Polyline and all of its
 *   children figures.
 * <LI>
 *   A {@link DelegatingLayout} is set as the default layout.  A delegating layout allows
 *   children to position themselves via {@link Locator Locators}.
 * </UL>
 * <P>
 */
public class PolylineConnection
    : Polyline
    , Connection, AnchorListener
{

// reimplement for Connection
PointList getPoints(){
    return super.getPoints();
}
// reimplement for Connection
void setPoints(PointList list){
    super.setPoints(list);
}


private ConnectionAnchor startAnchor, endAnchor;
private ConnectionRouter connectionRouter;
private RotatableDecoration startArrow, endArrow;

private void instanceInit(){
    connectionRouter = ConnectionRouter_NULL;
    setLayoutManager(new DelegatingLayout());
    addPoint(new Point(0, 0));
    addPoint(new Point(100, 100));
}

this(){
    instanceInit();
}
/**
 * Hooks the source and target anchors.
 * @see Figure#addNotify()
 */
public void addNotify() {
    super.addNotify();
    hookSourceAnchor();
    hookTargetAnchor();
}

/**
 * Appends the given routing listener to the list of listeners.
 * @param listener the routing listener
 * @since 3.2
 */
public void addRoutingListener(RoutingListener listener) {
    if (auto notifier = cast(RoutingNotifier)connectionRouter ) {
        notifier.listeners.add(cast(Object)listener);
    } else
        connectionRouter = new RoutingNotifier(connectionRouter, listener);
}

/**
 * Called by the anchors of this connection when they have moved, revalidating this
 * polyline connection.
 * @param anchor the anchor that moved
 */
public void anchorMoved(ConnectionAnchor anchor) {
    revalidate();
}

/**
 * Returns the bounds which holds all the points in this polyline connection. Returns any
 * previously existing bounds, else calculates by unioning all the children's
 * dimensions.
 * @return the bounds
 */
public Rectangle getBounds() {
    if (bounds is null) {
        super.getBounds();
        for (int i = 0; i < getChildren().size(); i++) {
            IFigure child = cast(IFigure)getChildren().get(i);
            bounds.union_(child.getBounds());
        }
    }
    return bounds;
}

/**
 * Returns the <code>ConnectionRouter</code> used to layout this connection. Will not
 * return <code>null</code>.
 * @return this connection's router
 */
public ConnectionRouter getConnectionRouter() {
    if (auto n = cast(RoutingNotifier)connectionRouter )
        return n.realRouter;
    return connectionRouter;
}

/**
 * Returns this connection's routing constraint from its connection router.  May return
 * <code>null</code>.
 * @return the connection's routing constraint
 */
public Object getRoutingConstraint() {
    if (getConnectionRouter() !is null)
        return getConnectionRouter().getConstraint(this);
    else
        return null;
}

/**
 * @return the anchor at the start of this polyline connection (may be null)
 */
public ConnectionAnchor getSourceAnchor() {
    return startAnchor;
}

/**
 * @return the source decoration (may be null)
 */
protected RotatableDecoration getSourceDecoration() {
    return startArrow;
}

/**
 * @return the anchor at the end of this polyline connection (may be null)
 */
public ConnectionAnchor getTargetAnchor() {
    return endAnchor;
}

/**
 * @return the target decoration (may be null)
 *
 * @since 2.0
 */
protected RotatableDecoration getTargetDecoration() {
    return endArrow;
}

private void hookSourceAnchor() {
    if (getSourceAnchor() !is null)
        getSourceAnchor().addAnchorListener(this);
}

private void hookTargetAnchor() {
    if (getTargetAnchor() !is null)
        getTargetAnchor().addAnchorListener(this);
}

/**
 * Layouts this polyline. If the start and end anchors are present, the connection router
 * is used to route this, after which it is laid out. It also fires a moved method.
 */
public void layout() {
    if (getSourceAnchor() !is null && getTargetAnchor() !is null)
        connectionRouter.route(this);

    Rectangle oldBounds = bounds;
    super.layout();
    bounds = null;

    if (!getBounds().contains(oldBounds)) {
        getParent().translateToParent(oldBounds);
        getUpdateManager().addDirtyRegion(getParent(), oldBounds);
    }

    repaint();
    fireFigureMoved();
}

/**
 * Called just before the receiver is being removed from its parent. Results in removing
 * itself from the connection router.
 *
 * @since 2.0
 */
public void removeNotify() {
    unhookSourceAnchor();
    unhookTargetAnchor();
    connectionRouter.remove(this);
    super.removeNotify();
}

/**
 * Removes the first occurence of the given listener.
 * @param listener the listener being removed
 * @since 3.2
 */
public void removeRoutingListener(RoutingListener listener) {
    if ( auto notifier = cast(RoutingNotifier)connectionRouter ) {
        notifier.listeners.remove(cast(Object)listener);
        if (notifier.listeners.isEmpty())
            connectionRouter = notifier.realRouter;
    }
}

/**
 * @see IFigure#revalidate()
 */
public void revalidate() {
    super.revalidate();
    connectionRouter.invalidate(this);
}

/**
 * Sets the connection router which handles the layout of this polyline. Generally set by
 * the parent handling the polyline connection.
 * @param cr the connection router
 */
public void setConnectionRouter(ConnectionRouter cr) {
    if (cr is null)
        cr = ConnectionRouter_NULL;
    ConnectionRouter oldRouter = getConnectionRouter();
    if (oldRouter !is cr) {
        connectionRouter.remove(this);
        if (auto n = cast(RoutingNotifier)connectionRouter )
            n.realRouter = cr;
        else
            connectionRouter = cr;
        firePropertyChange(Connection.PROPERTY_CONNECTION_ROUTER, cast(Object)oldRouter, cast(Object)cr);
        revalidate();
    }
}

/**
 * Sets the routing constraint for this connection.
 * @param cons the constraint
 */
public void setRoutingConstraint(Object cons) {
    if (connectionRouter !is null)
        connectionRouter.setConstraint(this, cons);
    revalidate();
}

/**
 * Sets the anchor to be used at the start of this polyline connection.
 * @param anchor the new source anchor
 */
public void setSourceAnchor(ConnectionAnchor anchor) {
    if (anchor is startAnchor)
        return;
    unhookSourceAnchor();
    //No longer needed, revalidate does this.
    //getConnectionRouter().invalidate(this);
    startAnchor = anchor;
    if (getParent() !is null)
        hookSourceAnchor();
    revalidate();
}

/**
 * Sets the decoration to be used at the start of the {@link Connection}.
 * @param dec the new source decoration
 * @since 2.0
 */
public void setSourceDecoration(RotatableDecoration dec) {
    if (startArrow is dec)
        return;
    if (startArrow !is null)
        remove(startArrow);
    startArrow = dec;
    if (startArrow !is null)
        add(startArrow, new ArrowLocator(this, ConnectionLocator.SOURCE));
}

/**
 * Sets the anchor to be used at the end of the polyline connection. Removes this listener
 * from the old anchor and adds it to the new anchor.
 * @param anchor the new target anchor
 */
public void setTargetAnchor(ConnectionAnchor anchor) {
    if (anchor is endAnchor)
        return;
    unhookTargetAnchor();
    //No longer needed, revalidate does this.
    //getConnectionRouter().invalidate(this);
    endAnchor = anchor;
    if (getParent() !is null)
        hookTargetAnchor();
    revalidate();
}

/**
 * Sets the decoration to be used at the end of the {@link Connection}.
 * @param dec the new target decoration
 */
public void setTargetDecoration(RotatableDecoration dec) {
    if (endArrow is dec)
        return;
    if (endArrow !is null)
        remove(endArrow);
    endArrow = dec;
    if (endArrow !is null)
        add(endArrow, new ArrowLocator(this, ConnectionLocator.TARGET));
}

private void unhookSourceAnchor() {
    if (getSourceAnchor() !is null)
        getSourceAnchor().removeAnchorListener(this);
}

private void unhookTargetAnchor() {
    if (getTargetAnchor() !is null)
        getTargetAnchor().removeAnchorListener(this);
}

final class RoutingNotifier : ConnectionRouter {

    ConnectionRouter realRouter;
    List listeners;

    this(ConnectionRouter router, RoutingListener listener) {
        listeners = new ArrayList(1);
        realRouter = router;
        listeners.add(cast(Object)listener);
    }

    public Object getConstraint(Connection connection) {
        return realRouter.getConstraint(connection);
    }

    public void invalidate(Connection connection) {
        for (int i = 0; i < listeners.size(); i++)
            (cast(RoutingListener)listeners.get(i)).invalidate(connection);

        realRouter.invalidate(connection);
    }

    public void route(Connection connection) {
        bool consumed = false;
        for (int i = 0; i < listeners.size(); i++)
            consumed |= (cast(RoutingListener)listeners.get(i)).route(connection);

        if (!consumed)
            realRouter.route(connection);

        for (int i = 0; i < listeners.size(); i++)
            (cast(RoutingListener)listeners.get(i)).postRoute(connection);
    }

    public void remove(Connection connection) {
        for (int i = 0; i < listeners.size(); i++)
            (cast(RoutingListener)listeners.get(i)).remove(connection);
        realRouter.remove(connection);
    }

    public void setConstraint(Connection connection, Object constraint) {
        for (int i = 0; i < listeners.size(); i++)
            (cast(RoutingListener)listeners.get(i)).setConstraint(connection, constraint);
        realRouter.setConstraint(connection, constraint);
    }

}

}