public abstract class GConnectionSkin extends GSkin<GConnection> {

    /**
     * Cache the index of this connection skin inside the list of children of the
     * connection layer. As the graph editor grows the indexOf() lookup calls take
     * up a considerable amount of time.
     */
    private int mConnectionIndex;

    /**
     * Creates a new {@link GConnectionSkin}.
     *
     * @param connection the {@link GConnection} represented by the skin
     */
    public GConnectionSkin(final GConnection connection) {
        super(connection);
    }

    /**
     * Sets the skin objects for all joints inside the connection.
     *
     * <p>
     * This will be called as the connection skin is created. The connection skin can manipulate its joint skins if it
     * chooses. For example a 'rectangular' connection skin may restrict the movement of the first and last joints to
     * the x direction only.
     * </p>
     *
     * @param jointSkins the list of all {@link GJointSkin} instances associated to the connection
     */
    public abstract void setJointSkins(final List<GJointSkin> jointSkins);

    /**
     * Draws the connection skin. This is called every time the connection's
     * position could change, for example if one of its connectors is moved,
     * after {@link #update()}.
     *
     * <p>
     * A simple connection skin may ignore the given parameter. This parameter
     * can for example be used to display a 'rerouting' effect when the
     * connection passes over another connection.
     * </p>
     *
     * @param allConnections
     *            the lists of points for all connections (can be ignored in a
     *            simple skin)
     */
    public void draw(@SuppressWarnings("unused") final Map<GConnectionSkin, Point2D[]> allConnections)
    {
        if (getRoot() != null && getRoot().getParent() != null)
        {
            mConnectionIndex = getRoot().getParent().getChildrenUnmodifiable().indexOf(getRoot());
        }
        else
        {
            mConnectionIndex = -1;
        }
    }

    /**
     * Update and return the points of this connection. This is called every
     * time the connection's position could change, for example if one of its
     * connectors is moved before {@link #draw(Map)}.
     * <p>
     * The order of the points is as follows:
     *
     * <ol>
     * <li>Source position.
     * <li>Joint positions in same order the joints appear in their
     * {@link GConnection}.
     * <li>Target position.
     * </ol>
     *
     * </p>
     *
     * <p>
     * This method is called on <b>all</b> connection skins <b>before</b> the
     * draw method is called on any connection skin. It can safely be ignored by
     * simple skin implementations.
     * </p>
     *
     * <p>
     * Overriding this method allows the connection skin to apply constraints to
     * its set of points, and these constraints will be taken into account
     * during the draw methods of other connections, even if they are drawn
     * before this connection.
     * </p>
     *
     * @return points
     */
    public Point2D[] update()
    {
        final GConnection item = getItem();
        final SkinLookup skinLookup = getGraphEditor() == null ? null : getGraphEditor().getSkinLookup();
        if (item == null || skinLookup == null)
        {
            return null;
        }
        else if (item.getJoints().isEmpty())
        {
            final Point2D[] points = new Point2D[2];

            // Start: Source position
            points[0] = GeometryUtils.getConnectorPosition(item.getSource(), skinLookup);

            // End: Target position
            points[1] = GeometryUtils.getConnectorPosition(item.getTarget(), skinLookup);

            return points;
        }
        else
        {
            final int len = item.getJoints().size() + 2;
            final Point2D[] points = new Point2D[len];

            // Middle: joint positions
            GeometryUtils.fillJointPositions(item, skinLookup, points);

            // Start: Source position
            points[0] = GeometryUtils.getConnectorPosition(item.getSource(), skinLookup);

            // End: Target position
            points[len - 1] = GeometryUtils.getConnectorPosition(item.getTarget(), skinLookup);

            return points;
        }
    }

    /**
     * @return cached position (index) of this connection skin inside the
     *         child-list of the parent connection layer.
     */
    public int getParentIndex()
    {
        return mConnectionIndex;
    }
} public abstract class GConnectorSkin extends GSkin<GConnector> {

    /**
     * Creates a new {@link GConnectorSkin}.
     *
     * @param connector the {@link GConnector} represented by the skin
     */
    public GConnectorSkin(final GConnector connector) {
        super(connector);
    }

    /**
     * Gets the width of the connector skin.
     *
     * @return the width of the connector skin
     */
    public abstract double getWidth();

    /**
     * Gets the height of the connector skin.
     *
     * @return the height of the connector skin
     */
    public abstract double getHeight();

    /**
     * Applys the specified style to the connector.
     *
     * <p>
     * This is called by the library during various mouse events. For example when a connector is dragged over another
     * connector in an attempt to create a new connection.
     * </p>
     *
     * @param style the {@link GConnectorStyle} to apply
     */
    public abstract void applyStyle(GConnectorStyle style);
} public interface GConnectorValidator {

    /**
     * Pre-validate check made during drag-over events.
     *
     * <p>
     * If the pre-validate check fails, the dragged connector will not interact with the dragged-over connector at all.
     * </p>
     *
     * @param source the {@link GConnector} that was dragged
     * @param target the {@link GConnector} that was dragged-over
     *
     * @return {@code true} if a validate check should be made, {@code false} if not
     */
    boolean prevalidate(GConnector source, GConnector target);

    /**
     * Validate check made during drag-over events. Only made if the pre-validate check passes.
     *
     * @param source the {@link GConnector} that was dragged
     * @param target the {@link GConnector} that was dragged-over
     *
     * @return {@code true} if connection is allowed, {@code false} if it is forbidden
     */
    boolean validate(GConnector source, GConnector target);

    /**
     * Creates the 'type' string to be used in a new connection.
     *
     * <p>
     * If both prevalidate and validate checks pass, a new connection will be created of the type returned by this
     * method.
     * </p>
     *
     * @param source the {@link GConnector} that was dragged
     * @param target the {@link GConnector} that was dragged-over
     *
     * @return a {@link String} specifying the type for the new connection
     */
    String createConnectionType(GConnector source, GConnector target);

    /**
     * Creates the 'type' string to be used in the joints inside a new connection.
     *
     * @param source the {@link GConnector} that was dragged
     * @param target the {@link GConnector} that was dragged-over
     *
     * @return a {@link String} specifying the type for the new connection
     */
    String createJointType(GConnector source, GConnector target);
}
 public abstract class GJointSkin extends GSkin<GJoint> {

    private final DraggableBox root = new DraggableBox(EditorElement.JOINT)
    {

        @Override
        public final void positionMoved()
        {
            super.positionMoved();
            GJointSkin.this.impl_positionMoved();
        }
    };

    /**
     * Creates a new {@link GJointSkin}.
     *
     * @param joint the {@link GJoint} represented by the skin
     */
    public GJointSkin(final GJoint joint) {
        super(joint);
    }

    /**
     * Gets the root JavaFX node of the skin.
     *
     * @return a {@link DraggableBox} containing the skin's root JavaFX node
     */
    @Override
    public DraggableBox getRoot() {
        return root;
    }

    /**
     * Initializes the joint skin.
     *
     * <p>
     * The skin's layout values are loaded from the {@link GJoint} at this point.
     * </p>
     */
    public void initialize() {
        getRoot().setLayoutX(getItem().getX() - getWidth() / 2);
        getRoot().setLayoutY(getItem().getY() - getHeight() / 2);
    }

    /**
     * Gets the width of the joint.
     *
     * <p>
     * For robust behaviour this should return the correct width at all times. Note that getRoot().getWidth() does not
     * meet this condition as it will return 0 until the joint's node is added to the scene graph and layed out.
     * </p>
     *
     * @return the width of the joint
     */
    public abstract double getWidth();

    /**
     * Gets the height of the joint.
     *
     * <p>
     * For robust behaviour this should return the correct height at all times. Note that getRoot().getHeight() does not
     * meet this condition as it will return 0 until the joint's node is added to the scene graph and layed out.
     * </p>
     *
     * @return the height of the joint
     */
    public abstract double getHeight();
} public abstract class GNodeSkin extends GSkin<GNode> {

    private final DraggableBox root;

    /**
     * Creates a new {@link GNodeSkin}.
     *
     * @param node the {@link GNode} represented by the skin
     */
    public GNodeSkin(final GNode node) {
        super(node);
        root = createContainer();
    }

    /**
     * Gets the root JavaFX node of the skin.
     *
     * @return a {@link ResizableBox} containing the skin's root JavaFX node
     */
    @Override
    public DraggableBox getRoot()
    {
        return root;
    }

    /**
     * Initializes the node skin.
     *
     * <p>
     * The skin's layout values, e.g. its x and y position, are loaded from the {@link GNode} at this point.
     * </p>
     */
    public void initialize() {

        getRoot().setLayoutX(getItem().getX());
        getRoot().setLayoutY(getItem().getY());

        getRoot().resize(getItem().getWidth(), getItem().getHeight());
    }

    /**
     * Sets the node's connector skins.
     *
     * <p>
     * This will be called as the node is created, or if a connector is added or removed. The connector skin's regions
     * should be added to the scene graph.
     * </p>
     *
     * @param connectorSkins a list of {@link GConnectorSkin} objects for each of the node's connectors
     */
    public abstract void setConnectorSkins(List<GConnectorSkin> connectorSkins);

    /**
     * Lays out the node's connectors.
     */
    public abstract void layoutConnectors();

    /**
     * Gets the position of the <b>center</b> of a connector relative to the node region.
     *
     * <p>
     * This will be the point where a connection will attach to.
     * </p>
     *
     * @param connectorSkin a {@link GConnectorSkin} instance
     *
     * @return the x and y coordinates of the connector
     */
    public abstract Point2D getConnectorPosition(GConnectorSkin connectorSkin);

    /**
     * Creates and returns the {@link DraggableBox} that serves as the root for
     * this node skin.<br>
     * By default a {@link ResizableBox} will be created and return as most
     * nodes will be both draggable and resizable.
     *
     * @return {@link DraggableBox}
     */
    protected DraggableBox createContainer()
    {
        return new ResizableBox(EditorElement.NODE)
        {

            @Override
            protected void layoutChildren()
            {
                super.layoutChildren();
                layoutConnectors();
            }

            @Override
            public void positionMoved()
            {
                super.positionMoved();
                GNodeSkin.this.impl_positionMoved();
            }
        };
    }
} public abstract class GTailSkin extends GSkin<GConnector> {

    /**
     * Creates a new {@link GTailSkin}.
     *
     * @param connector the {@link GConnector} that the tail will extend from
     */
    public GTailSkin(final GConnector connector) {
        super(connector);
    }

    /**
     * Updates the position of the tail according to the specified start and end points.
     *
     * <p>
     * This method will be called when a 'fresh' tail is created from an unoccupied connector.
     * </p>
     *
     * @param start a {@link Point2D} containing the start x and y values
     * @param end a {@link Point2D} containing the end x and y values
     */
    public abstract void draw(Point2D start, Point2D end);

    /**
     * Updates the position of the tail according to the specified start and end points.
     *
     * <p>
     * This method will be called when a tail is snapped to the target connector that the mouse is hovering over.
     * </p>
     *
     * @param start a {@link Point2D} containing the start x and y values
     * @param end a {@link Point2D} containing the end x and y values
     * @param target the target connector that the tail is snapping to
     * @param valid {@code true} if the connection is valid, {@code false} if invalid
     */
    public abstract void draw(Point2D start, Point2D end, GConnector target, boolean valid);

    /**
     * Updates the position of the tail according to the specified start points, end points, and joint positions.
     *
     * <p>
     * This method will be called when an existing connection is repositioned. The tail skin may use the position of the
     * old connection to decide how to position itself, or it may ignore this information.
     * </p>
     *
     * @param start a {@link Point2D} containing the start x and y values
     * @param end a {@link Point2D} containing the end x and y values
     * @param jointPositions the positions of the joints at the time the connection was removed
     */
    public abstract void draw(Point2D start, Point2D end, List<Point2D> jointPositions);

    /**
     * Updates the position of the tail according to the specified start points, end points, and joint positions.
     *
     * <p>
     * This method will be called when an existing connection is repositioned and the stail is snapped to a target
     * connector.
     * </p>
     *
     * @param start a {@link Point2D} containing the start x and y values
     * @param end a {@link Point2D} containing the end x and y values
     * @param jointPositions 
     * @param target the target connector that the tail is snapping to
     * @param valid {@code true} if the connection is valid, {@code false} if invalid
     */
    public abstract void draw(Point2D start, Point2D end, List<Point2D> jointPositions, GConnector target, boolean valid);

    /**
     * Allocates a list of joint positions for a new connection.
     *
     * <p>
     * When the tail is 'converted' into a connection during a successful drag-drop gesture, this method will be called
     * so that the new connection's joint positions can be based on the final position of the tail.
     * </p>
     *
     * @return a list of {@code Point2D} objects containing x and y values for a newly-created connection
     */
    public abstract List<Point2D> allocateJointPositions();
} public interface GraphEditor extends GraphEditorSkins {

    /**
     * Sets a custom connector validator.
     *
     * <p>
     * This will be used to decide which connections are allowed / forbidden during drag and drop events in the editor.
     * </p>
     *
     * @param validator a custom validator implements {@link GConnectorValidator}, or null to use the default validator
     */
    void setConnectorValidator(final GConnectorValidator validator);

    /**
     * Sets the graph model to be edited.
     *
     * @param model the {@link GModel} to be edited
     */
    void setModel(final GModel model);

    /**
     * Gets the graph model that is currently being edited.
     *
     * @return the {@link GModel} being edited, or {@code null} if no model was ever set
     */
    GModel getModel();

    /**
     * Reloads the graph model currently being edited.
     *
     * <p>
     * <b>Note: </b><br>
     * If the model is updated via EMF commands, as is recommended, it should rarely be necessary to call this method.
     * The model will be reloaded automatically via a command-stack listener.
     * </p>
     */
    void reload();

    /**
     * The property containing the graph model being edited.
     *
     * @return a property containing the {@link GModel} being edited
     */
    ObjectProperty<GModel> modelProperty();

    /**
     * Gets the view where the graph is displayed and edited.
     *
     * <p>
     * The view is a JavaFX {@link Region}. It should be added to the scene
     * graph in the usual way.
     * </p>
     *
     * @return the {@link Region} where the graph is displayed and edited
     */
    Region getView();

    /**
     * Gets the properties of the editor.
     *
     * <p>
     * This provides access to global properties such as:
     *
     * <ul>
     * <li>Show/hide alignment grid.</li>
     * <li>Toggle snap-to-grid on/off.</li>
     * <li>Toggle editor bounds on/off.</li>
     * </ul>
     *
     * </p>
     *
     * @return an {@link GraphEditorProperties} instance containing the properties of the editor
     */
    GraphEditorProperties getProperties();

    /**
     * Gets the skin lookup.
     *
     * <p>
     * The skin lookup is used to get any skin instance associated to a model element instance.
     * </p>
     *
     * @return a {@link SkinLookup} used to lookup skins
     */
    SkinLookup getSkinLookup();

    /**
     * Gets the selection manager.
     *
     * <p>
     * The selection manager keeps track of the selected nodes, connections,
     * etc.
     * </p>
     *
     * @return the {@link SelectionManager}
     */
    SelectionManager getSelectionManager();

    /**
     * Sets a method to be called when a connection is created in the editor.
     *
     * <p>
     * This can be used to append additional commands to the one that created the connection.
     * </p>
     *
     * @param consumer a consumer to append additional commands
     */
    void setOnConnectionCreated(Function<GConnection, Command> consumer);

    /**
     * Sets a method to be called when a connection is removed in the editor.
     *
     * <p>
     * This can be used to create additional commands to the one that removed
     * the connection.
     * </p>
     *
     * @param pOnConnectionRemoved
     *            a {@link BiFunction} creating the additional command
     */
    void setOnConnectionRemoved(BiFunction<RemoveContext, GConnection, Command> pOnConnectionRemoved);

    /**
     * Sets a method to be called when a node is removed in the editor.
     *
     * <p>
     * This can be used to create additional commands to the one that removed
     * the node.
     * </p>
     *
     * @param pOnNodeRemoved
     *            a {@link BiFunction} creating the additional command
     */
    void setOnNodeRemoved(BiFunction<RemoveContext, GNode, Command> pOnNodeRemoved);

    /**
     * Deletes all elements that are currently selected.
     *
     * @param pItems
     *            the items to remove from the graph
     */
    void delete(Collection<EObject> pItems);
} public interface SelectionManager {

    /**
     * Gets the list of currently selected nodes.
     *
     * <p>
     * This list is read-only. Nodes should be selected via {@link #select(EObject)}.
     * </p>
     *
     * @return the unmodifiable list of currently selected nodes
     */
    List<GNode> getSelectedNodes();
    
    /**
     * Gets the list of currently selected connections.
     *
     * <p>
     * This list is read-only. Connections should be selected via {@link #select(EObject)}.
     * </p>
     *
     * @return the unmodifiable list of currently selected connections
     */
    List<GConnection> getSelectedConnections();

    /**
     * Gets the list of currently selected joints.
     *
     * <p>
     * This list is read-only. Joints should be selected via {@link #select(EObject)}.
     * </p>
     *
     * @return the unmodifiable list of currently selected joints
     */
    List<GJoint> getSelectedJoints();
    
    /**
     * Convenience method to inform if the given object is currently selected. Is
     * functionally equivalent to calling
     * <code>getSelectedItems().contains(object)</code>.
     * 
     * @param object
     * @return {@code true} if the given index is selected, {@code false} otherwise.
     */
    boolean isSelected(EObject object);
    
    /**
     * Gets the {@link ObservableSet} of currently-selected items.
     *
     * <p>
     * This set is read-only. Items should be selected via {@link #select(EObject)}.
     * </p>
     *
     * @return the set of selected items
     */
    ObservableSet<EObject> getSelectedItems();
    
    /**
     * This method will attempt to select the given object.
     *
     * @param object The object to attempt to select in the underlying data model.
     */
    void select(EObject object);
    
    /**
     * Selects all selectable elements (nodes, joints, and connections) in the graph editor.
     */
    void selectAll();
    
    /**
     * This method will clear the selection of the given object.
     * If the given object is not selected, nothing will happen.
     *
     * @param object The selected item to deselect.
     */
    void clearSelection(EObject object);

    /**
     * Clears the selection, i.e. de-selects all elements.
     */
    void clearSelection();
} public interface SkinLookup {

    /**
     * Gets the skin for the given node.
     *
     * @param node a {@link GNode} instance
     *
     * @return the associated {@link GNodeSkin} instance
     */
    GNodeSkin lookupNode(final GNode node);

    /**
     * Gets the skin for the given connector.
     *
     * @param connector a {@link GConnector} instance
     *
     * @return the associated {@link GConnectorSkin} instance
     */
    GConnectorSkin lookupConnector(final GConnector connector);

    /**
     * Gets the skin for the given connection.
     *
     * @param connection a {@link GConnection} instance
     *
     * @return the associated {@link GConnectionSkin} instance
     */
    GConnectionSkin lookupConnection(final GConnection connection);

    /**
     * Gets the skin for the given joint.
     *
     * @param joint a {@link GJoint} instance
     *
     * @return the associated {@link GJointSkin} instance
     */
    GJointSkin lookupJoint(final GJoint joint);

    /**
     * Gets the tail skin for the given connector.
     *
     * @param connector a {@link GConnector} instance
     *
     * @return the associated {@link GTailSkin} instance
     */
    GTailSkin lookupTail(final GConnector connector);
} public class ConnectionEventManager {

    private Function<GConnection, Command> connectionCreatedHandler;
    private BiFunction<RemoveContext, GConnection, Command> connectionRemovedHandler;

    /**
     * Sets the handler to be called when connections are created.
     *
     * @param connectionCreatedHandler the handler to be called when connections are created
     */
    public void setOnConnectionCreated(final Function<GConnection, Command> connectionCreatedHandler)
    {
        this.connectionCreatedHandler = connectionCreatedHandler;
    }

    /**
     * Sets the handler to be called when connections are removed.
     *
     * @param connectionRemovedHandler the handler to be called when connections are removed
     */
    public void setOnConnectionRemoved(final BiFunction<RemoveContext, GConnection, Command> connectionRemovedHandler)
    {
        this.connectionRemovedHandler = connectionRemovedHandler;
    }

    /**
     * Calls the connection-created handler (if it exists) after a connection was created.
     *
     * @param connection the connection that was created
     * @return the compound command that created it
     */
    public Command notifyConnectionAdded(final GConnection connection)
    {
        return connectionCreatedHandler == null ? null : connectionCreatedHandler.apply(connection);
    }

    /**
     * Calls the connection-removed handler (if it exists) after a connection was removed.
     *
     * @param connection the connection that was removed
     * @return the compound command that removed it
     */
    public Command notifyConnectionRemoved(final GConnection connection)
    {
        if (connectionRemovedHandler == null)
        {
            return null;
        }
        final RemoveContext context = new RemoveContext();
        context.canRemove(connection);
        return connectionRemovedHandler.apply(context, connection);
    }
}
 public class GraphEditorSkinManager implements SkinManager
{

    private final GraphEditor mGraphEditor;
    private final GraphEditorView mView;

    private Callback<GNode, GNodeSkin> mNodeSkinFactory;
    private Callback<GConnector, GConnectorSkin> mConnectorSkinFactory;
    private Callback<GConnection, GConnectionSkin> mConnectionSkinFactory;
    private Callback<GJoint, GJointSkin> mJointSkinFactory;
    private Callback<GConnector, GTailSkin> mTailSkinFactory;

    private final Map<GNode, GNodeSkin> mNodeSkins = new HashMap<>();
    private final Map<GConnector, GConnectorSkin> mConnectorSkins = new HashMap<>();
    private final Map<GConnection, GConnectionSkin> mConnectionSkins = new HashMap<>();
    private final Map<GJoint, GJointSkin> mJointSkins = new HashMap<>();
    private final Map<GConnector, GTailSkin> mTailSkins = new HashMap<>();

    private ConnectionLayouter mConnectionLayouter;
    private final Consumer<GSkin<?>> mOnPositionMoved = this::positionMoved;

    /**
     * Creates a new skin manager instance. Only one instance should exist per
     * {@link DefaultGraphEditor} instance.
     *
     * @param pGraphEditor
     *            {@link GraphEditor}
     * @param pView
     *            {@link GraphEditorView}
     */
    public GraphEditorSkinManager(final GraphEditor pGraphEditor, final GraphEditorView pView)
    {
        mView = pView;
        mGraphEditor = pGraphEditor;
    }

    @Override
    public void setConnectionLayouter(final ConnectionLayouter pConnectionLayouter)
    {
        mConnectionLayouter = pConnectionLayouter;
    }

    @Override
    public void setNodeSkinFactory(final Callback<GNode, GNodeSkin> pSkinFactory)
    {
        mNodeSkinFactory = pSkinFactory;
    }

    @Override
    public void setConnectorSkinFactory(final Callback<GConnector, GConnectorSkin> pConnectorSkinFactory)
    {
        mConnectorSkinFactory = pConnectorSkinFactory;
    }

    @Override
    public void setConnectionSkinFactory(final Callback<GConnection, GConnectionSkin> pConnectionSkinFactory)
    {
        mConnectionSkinFactory = pConnectionSkinFactory;
    }

    @Override
    public void setJointSkinFactory(final Callback<GJoint, GJointSkin> pJointSkinFactory)
    {
        mJointSkinFactory = pJointSkinFactory;
    }

    @Override
    public void setTailSkinFactory(final Callback<GConnector, GTailSkin> pTailSkinFactory)
    {
        mTailSkinFactory = pTailSkinFactory;
    }

    @Override
    public void clear()
    {
        if (!mNodeSkins.isEmpty())
        {
            final GNode[] nodes = mNodeSkins.keySet().toArray(new GNode[0]);
            for (final GNode n : nodes)
            {
                removeNode(n);
            }
        }

        if (!mConnectorSkins.isEmpty())
        {
            final GConnector[] connectors = mConnectorSkins.keySet().toArray(new GConnector[0]);
            for (final GConnector c : connectors)
            {
                removeConnector(c);
            }
        }

        if (!mConnectionSkins.isEmpty())
        {
            final GConnection[] connections = mConnectionSkins.keySet().toArray(new GConnection[0]);
            for (final GConnection c : connections)
            {
                removeConnection(c);
            }
        }

        if (!mJointSkins.isEmpty())
        {
            final GJoint[] joints = mJointSkins.keySet().toArray(new GJoint[0]);
            for (final GJoint c : joints)
            {
                removeJoint(c);
            }
        }

        if (!mTailSkins.isEmpty())
        {
            final GTailSkin[] tails = mTailSkins.values().toArray(new GTailSkin[0]);
            for (final GTailSkin tail : tails)
            {
                mView.remove(tail);
                tail.dispose();
            }
        }

        // remove any remainders that might have been left over:
        mView.clear();
    }

    @Override
    public void removeNode(final GNode pNodeToRemove)
    {
        if (pNodeToRemove != null)
        {
            final GNodeSkin removedSkin = mNodeSkins.remove(pNodeToRemove);
            if (removedSkin != null)
            {
                mView.remove(removedSkin);
                removedSkin.dispose();
            }

            for (int i = 0; i < pNodeToRemove.getConnectors().size(); i++)
            {
                removeConnector(pNodeToRemove.getConnectors().get(i));
            }
        }
    }

    @Override
    public void removeConnector(final GConnector pConnectorToRemove)
    {
        if (pConnectorToRemove != null)
        {
            final GConnectorSkin removedSkin = mConnectorSkins.remove(pConnectorToRemove);
            if (removedSkin != null)
            {
                removedSkin.dispose();
            }
            final GTailSkin removedTailSkin = mTailSkins.remove(pConnectorToRemove);
            if (removedTailSkin != null)
            {
                removedTailSkin.dispose();
            }
        }
    }

    @Override
    public void removeConnection(final GConnection pConnectionToRemove)
    {
        if (pConnectionToRemove != null)
        {
            final GConnectionSkin removedSkin = mConnectionSkins.remove(pConnectionToRemove);
            if (removedSkin != null)
            {
                mView.remove(removedSkin);
                removedSkin.dispose();
            }
        }
    }

    @Override
    public void removeJoint(final GJoint pJointToRemove)
    {
        if (pJointToRemove != null)
        {
            final GJointSkin removedSkin = mJointSkins.remove(pJointToRemove);
            if (removedSkin != null)
            {
                mView.remove(removedSkin);
                removedSkin.dispose();
            }
        }
    }

    @Override
    public void updateConnectors(final GNode pNode)
    {
        final GNodeSkin nodeSkin = mNodeSkins.get(pNode);
        if (nodeSkin != null)
        {
            final List<GConnectorSkin> nodeConnectorSkins = pNode.getConnectors().stream().map(this::lookupConnector)
                    .collect(Collectors.toList());
            nodeSkin.setConnectorSkins(nodeConnectorSkins);
        }
    }

    @Override
    public void updateJoints(final GConnection pConnection)
    {
        final GConnectionSkin connectionSkin = lookupConnection(pConnection);
        if (connectionSkin != null)
        {
            final List<GJointSkin> connectionJointSkins = pConnection.getJoints().stream().map(this::lookupJoint)
                    .collect(Collectors.toList());
            connectionSkin.setJointSkins(connectionJointSkins);
        }
    }

    @Override
    public GNodeSkin lookupOrCreateNode(final GNode pNode)
    {
        return mNodeSkins.computeIfAbsent(pNode, this::createNodeSkin);
    }

    @Override
    public GConnectorSkin lookupOrCreateConnector(final GConnector pConnector)
    {
        return mConnectorSkins.computeIfAbsent(pConnector, this::createConnectorSkin);
    }

    @Override
    public GConnectionSkin lookupOrCreateConnection(final GConnection pConnection)
    {
        return mConnectionSkins.computeIfAbsent(pConnection, this::createConnectionSkin);
    }

    @Override
    public GJointSkin lookupOrCreateJoint(final GJoint pJoint)
    {
        return mJointSkins.computeIfAbsent(pJoint, this::createJointSkin);
    }

    @Override
    public GNodeSkin lookupNode(final GNode pNode)
    {
        return mNodeSkins.get(pNode);
    }

    @Override
    public GConnectorSkin lookupConnector(final GConnector pConnector)
    {
        return mConnectorSkins.get(pConnector);
    }

    @Override
    public GConnectionSkin lookupConnection(final GConnection pConnection)
    {
        return mConnectionSkins.get(pConnection);
    }

    @Override
    public GJointSkin lookupJoint(final GJoint pJoint)
    {
        return mJointSkins.get(pJoint);
    }

    @Override
    public GTailSkin lookupTail(final GConnector pConnector)
    {
        // GTailSkin is always/only created on demand
        return mTailSkins.computeIfAbsent(pConnector, this::createTailSkin);
    }

    private GConnectorSkin createConnectorSkin(final GConnector pConnector)
    {
        GConnectorSkin skin = mConnectorSkinFactory == null ? null : mConnectorSkinFactory.call(pConnector);
        if (skin == null)
        {
            skin = new DefaultConnectorSkin(pConnector);
        }
        skin.setGraphEditor(mGraphEditor);
        return skin;
    }

    private GTailSkin createTailSkin(final GConnector pConnector)
    {
        GTailSkin skin = mTailSkinFactory == null ? null : mTailSkinFactory.call(pConnector);
        if (skin == null)
        {
            skin = new DefaultTailSkin(pConnector);
        }
        skin.setGraphEditor(mGraphEditor);
        return skin;
    }

    private GConnectionSkin createConnectionSkin(final GConnection pConnection)
    {
        GConnectionSkin skin = mConnectionSkinFactory == null ? null : mConnectionSkinFactory.call(pConnection);
        if (skin == null)
        {
            skin = new DefaultConnectionSkin(pConnection);
        }
        skin.setGraphEditor(mGraphEditor);
        if (!(skin instanceof VirtualSkin))
        {
            mView.add(skin);
        }
        return skin;
    }

    private GJointSkin createJointSkin(final GJoint pJoint)
    {
        GJointSkin skin = mJointSkinFactory == null ? null : mJointSkinFactory.call(pJoint);
        if (skin == null)
        {
            skin = new DefaultJointSkin(pJoint);
        }
        skin.setGraphEditor(mGraphEditor);
        skin.getRoot().setEditorProperties(mGraphEditor.getProperties());
        skin.impl_setOnPositionMoved(mOnPositionMoved);
        skin.initialize();
        if (!(skin instanceof VirtualSkin))
        {
            mView.add(skin);
        }
        return skin;
    }

    private GNodeSkin createNodeSkin(final GNode pNode)
    {
        GNodeSkin skin = mNodeSkinFactory == null ? null : mNodeSkinFactory.call(pNode);
        if (skin == null)
        {
            skin = new DefaultNodeSkin(pNode);
        }
        skin.setGraphEditor(mGraphEditor);
        skin.getRoot().setEditorProperties(mGraphEditor.getProperties());
        skin.impl_setOnPositionMoved(mOnPositionMoved);
        skin.initialize();
        if (!(skin instanceof VirtualSkin))
        {
            mView.add(skin);
        }
        return skin;
    }

    private void positionMoved(final GSkin<?> pMovedSkin)
    {
        final ConnectionLayouter layouter = mConnectionLayouter;
        if (layouter == null)
        {
            return;
        }
        if (pMovedSkin instanceof GNodeSkin gns)
        {
            // redraw all connections attached to each connector of the GNode:
            for (final GConnector connector : gns.getItem().getConnectors())
            {
                layouter.redraw(connector.getConnections());
            }
        }
        else if (pMovedSkin instanceof GJointSkin gjs)
        {
            // redraw the GConnection of the GJoint:
            layouter.redraw(gjs.getItem().getConnection());
        }
    }
} public interface ConnectionLayouter
{

    /**
     * Initializes the connection layout manager for the given model.
     *
     * @param pModel
     *            the {@link GModel} currently being edited
     */
    void initialize(final GModel pModel);

    /**
     * Draws all connections according to the latest layout values.
     */
    void draw();

    /**
     * Marks the complete connection layout as dirty. The next layout pass will
     * re-layout all connections (during {@link #draw()}).
     *
     * @since 31.01.2019
     */
    void redrawAll();

    /**
     * Redraw the given connection according to the latest layout values.
     *
     * @param pConnection
     *            connection to redraw
     */
    void redraw(final GConnection pConnection);

    /**
     * Redraw the given connections according to the latest layout values.
     *
     * @param pConnections
     *            connections to redraw
     */
    void redraw(final Collection<GConnection> pConnections);
} public class GraphEditorView extends Region
{

    private static final String STYLE_CLASS = "graph-editor";
    private static final String STYLE_CLASS_NODE_LAYER = "graph-editor-node-layer";
    private static final String STYLE_CLASS_CONNECTION_LAYER = "graph-editor-connection-layer";

    private final Pane mNodeLayer = new Pane();

    private final Pane mConnectionLayer = new Pane()
    {

        @Override
        protected void layoutChildren()
        {
            super.layoutChildren();
            drawConnections();
        }
    };

    private final GraphEditorGrid mGrid = new GraphEditorGrid();
    private ConnectionLayouter mConnectionLayouter;

    private final SelectionBox mSelectionBox = new SelectionBox();

    private final GraphEditorProperties mEditorProperties;

    /**
     * Creates a new {@link GraphEditorView} to which skin instances can be
     * added and removed.
     */
    public GraphEditorView(final GraphEditorProperties pEditorProperties)
    {
        getStyleClass().addAll(STYLE_CLASS);

        setMaxWidth(GraphEditorProperties.DEFAULT_MAX_WIDTH);
        setMaxHeight(GraphEditorProperties.DEFAULT_MAX_HEIGHT);

        initializeLayers();

        mEditorProperties = pEditorProperties;

        if (mEditorProperties != null)
        {
            mGrid.visibleProperty().bind(mEditorProperties.gridVisibleProperty());
            mGrid.gridSpacingProperty().bind(mEditorProperties.gridSpacingProperty());
        }
    }

    /**
     * Sets the connection-layouter to be used by the view.
     *
     * @param pConnectionLayouter
     *            the graph editor's {@link ConnectionLayouter} instance
     */
    public void setConnectionLayouter(final ConnectionLayouter pConnectionLayouter)
    {
        mConnectionLayouter = pConnectionLayouter;
    }

    /**
     * Clears all elements from the view.
     */
    public void clear()
    {
        mNodeLayer.getChildren().clear();
        mConnectionLayer.getChildren().clear();
    }

    /**
     * Adds a node skin to the view.
     *
     * @param pNodeSkin
     *            the {@link GNodeSkin} instance to be added
     */
    public void add(final GNodeSkin pNodeSkin)
    {
        if (pNodeSkin != null && !(pNodeSkin instanceof VirtualSkin))
        {
            mNodeLayer.getChildren().add(pNodeSkin.getRoot());
        }
    }

    /**
     * Adds a connection skin to the view.
     *
     * @param pConnectionSkin
     *            the {@link GConnectionSkin} instance to be added
     */
    public void add(final GConnectionSkin pConnectionSkin)
    {
        if (pConnectionSkin != null && !(pConnectionSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().add(0, pConnectionSkin.getRoot());
        }
    }

    /**
     * Adds a joint skin to the view.
     *
     * @param pJointSkin
     *            the {@link GJointSkin} instance to be added
     */
    public void add(final GJointSkin pJointSkin)
    {
        if (pJointSkin != null && !(pJointSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().add(pJointSkin.getRoot());
        }
    }

    /**
     * Adds a tail skin to the view.
     *
     * @param pTailSkin
     *            the {@link GTailSkin} instance to be added
     */
    public void add(final GTailSkin pTailSkin)
    {
        if (pTailSkin != null && !(pTailSkin instanceof VirtualSkin))
        {
            // add to back:
            mConnectionLayer.getChildren().add(0, pTailSkin.getRoot());
        }
    }

    /**
     * Removes a node skin from the view. Does nothing if the skin is not
     * present.
     *
     * @param pNodeSkin
     *            the {@link GNodeSkin} instance to remove
     */
    public void remove(final GNodeSkin pNodeSkin)
    {
        if (pNodeSkin != null && !(pNodeSkin instanceof VirtualSkin))
        {
            mNodeLayer.getChildren().remove(pNodeSkin.getRoot());
        }
    }

    /**
     * Removes a connection skin from the view. Does nothing if the skin is not
     * present.
     *
     * @param pConnectionSkin
     *            the {@link GConnectionSkin} instance to remove
     */
    public void remove(final GConnectionSkin pConnectionSkin)
    {
        if (pConnectionSkin != null && !(pConnectionSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().remove(pConnectionSkin.getRoot());
        }
    }

    /**
     * Removes a joint skin from the view. Does nothing if the skin is not
     * present.
     *
     * @param pJointSkin
     *            the {@link GJointSkin} instance to remove
     */
    public void remove(final GJointSkin pJointSkin)
    {
        if (pJointSkin != null && !(pJointSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().remove(pJointSkin.getRoot());
        }
    }

    /**
     * Removes a tail skin from the view. Does nothing if the skin is not
     * present.
     *
     * @param pTailSkin
     *            the {@link GTailSkin} instance to remove
     */
    public void remove(final GTailSkin pTailSkin)
    {
        if (pTailSkin != null && !(pTailSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().remove(pTailSkin.getRoot());
        }
    }

    /**
     * Gets the editor properties instance used by the view.
     *
     * @return editorProperties the {@link GraphEditorProperties} instance used
     *         by the view
     */
    public GraphEditorProperties getEditorProperties()
    {
        return mEditorProperties;
    }

    /**
     * Draws a selection box in the view.
     *
     * @param pX
     *            the x position of the selection box
     * @param pY
     *            the y position of the selection box
     * @param pWidth
     *            the width of the selection box
     * @param pHeight
     *            the height of the selection box
     */
    public void drawSelectionBox(final double pX, final double pY, final double pWidth, final double pHeight)
    {
        mSelectionBox.draw(pX, pY, pWidth, pHeight);
    }

    /**
     * Hides the selection box.
     */
    public void hideSelectionBox()
    {
        mSelectionBox.setVisible(false);
    }

    @Override
    protected void layoutChildren()
    {
        final double width = getWidth();
        final double height = getHeight();
        mNodeLayer.resizeRelocate(0, 0, width, height);
        mConnectionLayer.resizeRelocate(0, 0, width, height);
        mGrid.resizeRelocate(0, 0, width, height);
        drawConnections();
    }

    /**
     * calls {@link ConnectionLayouter#draw()}
     *
     * @since 31.01.2019
     */
    void drawConnections()
    {
        if (mConnectionLayouter != null)
        {
            mConnectionLayouter.draw();
        }
    }

    /**
     * Initializes the two layers (node and connection) that the view is
     * composed of.
     */
    private void initializeLayers()
    {
        mNodeLayer.setPickOnBounds(false);
        mConnectionLayer.setPickOnBounds(false);

        mNodeLayer.getStyleClass().add(STYLE_CLASS_NODE_LAYER);
        mConnectionLayer.getStyleClass().add(STYLE_CLASS_CONNECTION_LAYER);

        // Node layer should be on top of connection layer, so we add it second.
        getChildren().addAll(mGrid, mConnectionLayer, mNodeLayer, mSelectionBox);
    }
} public interface GConnection extends EObject {
	/**
	 * Returns the value of the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Id</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Id</em>' attribute.
	 * @see #setId(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Id()
	 * @model id="true"
	 * @generated
	 */
	String getId();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnection#getId <em>Id</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Id</em>' attribute.
	 * @see #getId()
	 * @generated
	 */
	void setId(String value);

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnection#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>Source</b></em>' reference.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Source</em>' reference isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Source</em>' reference.
	 * @see #setSource(GConnector)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Source()
	 * @model required="true"
	 * @generated
	 */
	GConnector getSource();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnection#getSource <em>Source</em>}' reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Source</em>' reference.
	 * @see #getSource()
	 * @generated
	 */
	void setSource(GConnector value);

	/**
	 * Returns the value of the '<em><b>Target</b></em>' reference.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Target</em>' reference isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Target</em>' reference.
	 * @see #setTarget(GConnector)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Target()
	 * @model required="true"
	 * @generated
	 */
	GConnector getTarget();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnection#getTarget <em>Target</em>}' reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Target</em>' reference.
	 * @see #getTarget()
	 * @generated
	 */
	void setTarget(GConnector value);

	/**
	 * Returns the value of the '<em><b>Joints</b></em>' containment reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GJoint}.
	 * It is bidirectional and its opposite is '{@link de.tesis.dynaware.grapheditor.model.GJoint#getConnection <em>Connection</em>}'.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Joints</em>' containment reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Joints</em>' containment reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Joints()
	 * @see de.tesis.dynaware.grapheditor.model.GJoint#getConnection
	 * @model opposite="connection" containment="true"
	 * @generated
	 */
	EList<GJoint> getJoints();

} // GConnection public interface GConnector extends EObject {
	/**
	 * Returns the value of the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Id</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Id</em>' attribute.
	 * @see #setId(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Id()
	 * @model id="true"
	 * @generated
	 */
	String getId();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getId <em>Id</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Id</em>' attribute.
	 * @see #getId()
	 * @generated
	 */
	void setId(String value);

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>Parent</b></em>' container reference.
	 * It is bidirectional and its opposite is '{@link de.tesis.dynaware.grapheditor.model.GNode#getConnectors <em>Connectors</em>}'.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Parent</em>' container reference isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Parent</em>' container reference.
	 * @see #setParent(GNode)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Parent()
	 * @see de.tesis.dynaware.grapheditor.model.GNode#getConnectors
	 * @model opposite="connectors" required="true" transient="false"
	 * @generated
	 */
	GNode getParent();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getParent <em>Parent</em>}' container reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Parent</em>' container reference.
	 * @see #getParent()
	 * @generated
	 */
	void setParent(GNode value);

	/**
	 * Returns the value of the '<em><b>Connections</b></em>' reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GConnection}.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connections</em>' reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connections</em>' reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Connections()
	 * @model
	 * @generated
	 */
	EList<GConnection> getConnections();

	/**
	 * Returns the value of the '<em><b>X</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>X</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>X</em>' attribute.
	 * @see #setX(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_X()
	 * @model
	 * @generated
	 */
	double getX();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getX <em>X</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>X</em>' attribute.
	 * @see #getX()
	 * @generated
	 */
	void setX(double value);

	/**
	 * Returns the value of the '<em><b>Y</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Y</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Y</em>' attribute.
	 * @see #setY(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Y()
	 * @model
	 * @generated
	 */
	double getY();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getY <em>Y</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Y</em>' attribute.
	 * @see #getY()
	 * @generated
	 */
	void setY(double value);

	/**
	 * Returns the value of the '<em><b>Connection Detached On Drag</b></em>' attribute.
	 * The default value is <code>"true"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connection Detached On Drag</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connection Detached On Drag</em>' attribute.
	 * @see #setConnectionDetachedOnDrag(boolean)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_ConnectionDetachedOnDrag()
	 * @model default="true" required="true"
	 * @generated
	 */
	boolean isConnectionDetachedOnDrag();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#isConnectionDetachedOnDrag <em>Connection Detached On Drag</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Connection Detached On Drag</em>' attribute.
	 * @see #isConnectionDetachedOnDrag()
	 * @generated
	 */
	void setConnectionDetachedOnDrag(boolean value);

} // GConnector public interface GJoint extends EObject {
	/**
	 * Returns the value of the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Id</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Id</em>' attribute.
	 * @see #setId(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_Id()
	 * @model id="true"
	 * @generated
	 */
	String getId();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getId <em>Id</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Id</em>' attribute.
	 * @see #getId()
	 * @generated
	 */
	void setId(String value);

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>Connection</b></em>' container reference.
	 * It is bidirectional and its opposite is '{@link de.tesis.dynaware.grapheditor.model.GConnection#getJoints <em>Joints</em>}'.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connection</em>' container reference isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connection</em>' container reference.
	 * @see #setConnection(GConnection)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_Connection()
	 * @see de.tesis.dynaware.grapheditor.model.GConnection#getJoints
	 * @model opposite="joints" required="true" transient="false"
	 * @generated
	 */
	GConnection getConnection();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getConnection <em>Connection</em>}' container reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Connection</em>' container reference.
	 * @see #getConnection()
	 * @generated
	 */
	void setConnection(GConnection value);

	/**
	 * Returns the value of the '<em><b>X</b></em>' attribute.
	 * The default value is <code>"0"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>X</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>X</em>' attribute.
	 * @see #setX(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_X()
	 * @model default="0" required="true"
	 * @generated
	 */
	double getX();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getX <em>X</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>X</em>' attribute.
	 * @see #getX()
	 * @generated
	 */
	void setX(double value);

	/**
	 * Returns the value of the '<em><b>Y</b></em>' attribute.
	 * The default value is <code>"0"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Y</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Y</em>' attribute.
	 * @see #setY(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_Y()
	 * @model default="0" required="true"
	 * @generated
	 */
	double getY();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getY <em>Y</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Y</em>' attribute.
	 * @see #getY()
	 * @generated
	 */
	void setY(double value);

} // GJoint public interface GModel extends EObject {
	/**
	 * Returns the value of the '<em><b>Nodes</b></em>' containment reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GNode}.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Nodes</em>' containment reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Nodes</em>' containment reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_Nodes()
	 * @model containment="true"
	 * @generated
	 */
	EList<GNode> getNodes();

	/**
	 * Returns the value of the '<em><b>Connections</b></em>' containment reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GConnection}.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connections</em>' containment reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connections</em>' containment reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_Connections()
	 * @model containment="true"
	 * @generated
	 */
	EList<GConnection> getConnections();

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GModel#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>Content Width</b></em>' attribute.
	 * The default value is <code>"3000"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Content Width</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Content Width</em>' attribute.
	 * @see #setContentWidth(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_ContentWidth()
	 * @model default="3000" required="true"
	 * @generated
	 */
	double getContentWidth();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GModel#getContentWidth <em>Content Width</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Content Width</em>' attribute.
	 * @see #getContentWidth()
	 * @generated
	 */
	void setContentWidth(double value);

	/**
	 * Returns the value of the '<em><b>Content Height</b></em>' attribute.
	 * The default value is <code>"2250"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Content Height</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Content Height</em>' attribute.
	 * @see #setContentHeight(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_ContentHeight()
	 * @model default="2250" required="true"
	 * @generated
	 */
	double getContentHeight();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GModel#getContentHeight <em>Content Height</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Content Height</em>' attribute.
	 * @see #getContentHeight()
	 * @generated
	 */
	void setContentHeight(double value);

} // GModel public interface GNode extends EObject {
	/**
	 * Returns the value of the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Id</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Id</em>' attribute.
	 * @see #setId(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Id()
	 * @model id="true"
	 * @generated
	 */
	String getId();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getId <em>Id</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Id</em>' attribute.
	 * @see #getId()
	 * @generated
	 */
	void setId(String value);

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>X</b></em>' attribute.
	 * The default value is <code>"0"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>X</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>X</em>' attribute.
	 * @see #setX(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_X()
	 * @model default="0" required="true"
	 * @generated
	 */
	double getX();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getX <em>X</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>X</em>' attribute.
	 * @see #getX()
	 * @generated
	 */
	void setX(double value);

	/**
	 * Returns the value of the '<em><b>Y</b></em>' attribute.
	 * The default value is <code>"0"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Y</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Y</em>' attribute.
	 * @see #setY(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Y()
	 * @model default="0" required="true"
	 * @generated
	 */
	double getY();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getY <em>Y</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Y</em>' attribute.
	 * @see #getY()
	 * @generated
	 */
	void setY(double value);

	/**
	 * Returns the value of the '<em><b>Width</b></em>' attribute.
	 * The default value is <code>"151"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Width</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Width</em>' attribute.
	 * @see #setWidth(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Width()
	 * @model default="151" required="true"
	 * @generated
	 */
	double getWidth();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getWidth <em>Width</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Width</em>' attribute.
	 * @see #getWidth()
	 * @generated
	 */
	void setWidth(double value);

	/**
	 * Returns the value of the '<em><b>Height</b></em>' attribute.
	 * The default value is <code>"101"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Height</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Height</em>' attribute.
	 * @see #setHeight(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Height()
	 * @model default="101" required="true"
	 * @generated
	 */
	double getHeight();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getHeight <em>Height</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Height</em>' attribute.
	 * @see #getHeight()
	 * @generated
	 */
	void setHeight(double value);

	/**
	 * Returns the value of the '<em><b>Connectors</b></em>' containment reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GConnector}.
	 * It is bidirectional and its opposite is '{@link de.tesis.dynaware.grapheditor.model.GConnector#getParent <em>Parent</em>}'.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connectors</em>' containment reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connectors</em>' containment reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Connectors()
	 * @see de.tesis.dynaware.grapheditor.model.GConnector#getParent
	 * @model opposite="parent" containment="true"
	 * @generated
	 */
	EList<GConnector> getConnectors();

} // GNode public class GraphEditorProperties implements GraphEventManager
{

    /**
     * The default max width of the editor region, set on startup.
     */
    public static final double DEFAULT_MAX_WIDTH = Double.MAX_VALUE;
    /**
     * The default max height of the editor region, set on startup.
     */
    public static final double DEFAULT_MAX_HEIGHT = Double.MAX_VALUE;

    public static final double DEFAULT_BOUND_VALUE = 15;
    public static final double DEFAULT_GRID_SPACING = 12;

    // The distance from the editor edge at which the objects should stop when dragged / resized.
    private double northBoundValue = DEFAULT_BOUND_VALUE;
    private double southBoundValue = DEFAULT_BOUND_VALUE;
    private double eastBoundValue = DEFAULT_BOUND_VALUE;
    private double westBoundValue = DEFAULT_BOUND_VALUE;

    // Off by default.
    private final BooleanProperty gridVisible = new SimpleBooleanProperty(this, "gridVisible"); //$NON-NLS-1$
    private final BooleanProperty snapToGrid = new SimpleBooleanProperty(this, "snapToGrid"); //$NON-NLS-1$
    private final DoubleProperty gridSpacing = new SimpleDoubleProperty(this, "gridSpacing", DEFAULT_GRID_SPACING); //$NON-NLS-1$

    private final Map<EditorElement, BooleanProperty> readOnly = new EnumMap<>(EditorElement.class);

    private final ObservableMap<String, String> customProperties = FXCollections.observableHashMap();

    private final GraphEventManager eventManager = new GraphEventManagerImpl();

    /**
     * Creates a new editor properties instance containing a set of default
     * properties.
     */
    public GraphEditorProperties()
    {
    }

    /**
     * Copy constructor.
     *
     * <p>
     * Creates a new editor properties instance with all values copied over from
     * an existing instance.
     * </p>
     *
     * @param editorProperties
     *         an existing {@link GraphEditorProperties} instance
     */
    public GraphEditorProperties(final GraphEditorProperties editorProperties)
    {
        northBoundValue = editorProperties.getNorthBoundValue();
        southBoundValue = editorProperties.getSouthBoundValue();
        eastBoundValue = editorProperties.getEastBoundValue();
        westBoundValue = editorProperties.getWestBoundValue();

        gridVisible.set(editorProperties.isGridVisible());
        snapToGrid.set(editorProperties.isSnapToGridOn());
        gridSpacing.set(editorProperties.getGridSpacing());

        for (final Map.Entry<EditorElement, BooleanProperty> entry : editorProperties.readOnly.entrySet())
        {
            readOnly.computeIfAbsent(entry.getKey(), k -> new SimpleBooleanProperty()).set(entry.getValue().get());
        }

        customProperties.putAll(editorProperties.getCustomProperties());
    }

    /**
     * Gets the value of the north bound.
     *
     * @return the value of the north bound
     */
    public double getNorthBoundValue()
    {
        return northBoundValue;
    }

    /**
     * Sets the value of the north bound.
     *
     * @param pNorthBoundValue
     *         the value of the north bound
     */
    public void setNorthBoundValue(final double pNorthBoundValue)
    {
        northBoundValue = pNorthBoundValue;
    }

    /**
     * Gets the value of the south bound.
     *
     * @return the value of the south bound
     */
    public double getSouthBoundValue()
    {
        return southBoundValue;
    }

    /**
     * Sets the value of the south bound.
     *
     * @param pSouthBoundValue
     *         the value of the south bound
     */
    public void setSouthBoundValue(final double pSouthBoundValue)
    {
        southBoundValue = pSouthBoundValue;
    }

    /**
     * Gets the value of the east bound.
     *
     * @return the value of the east bound
     */
    public double getEastBoundValue()
    {
        return eastBoundValue;
    }

    /**
     * Sets the value of the east bound.
     *
     * @param pEastBoundValue
     *         the value of the east bound
     */
    public void setEastBoundValue(final double pEastBoundValue)
    {
        eastBoundValue = pEastBoundValue;
    }

    /**
     * Gets the value of the west bound.
     *
     * @return the value of the west bound
     */
    public double getWestBoundValue()
    {
        return westBoundValue;
    }

    /**
     * Sets the value of the west bound.
     *
     * @param pWestBoundValue
     *         the value of the west bound
     */
    public void setWestBoundValue(final double pWestBoundValue)
    {
        westBoundValue = pWestBoundValue;
    }

    /**
     * Checks if the background grid is visible.
     *
     * @return {@code true} if the background grid is visible, {@code false} if
     *         not
     */
    public boolean isGridVisible()
    {
        return gridVisible.get();
    }

    /**
     * Sets whether the background grid should be visible or not.
     *
     * @param pGridVisible
     *            {@code true} if the background grid should be visible,
     *            {@code false} if not
     */
    public void setGridVisible(final boolean pGridVisible)
    {
        gridVisible.set(pGridVisible);
    }

    /**
     * Gets the grid-visible property.
     *
     * @return a {@link BooleanProperty} tracking whether the grid is visible or
     *         not
     */
    public BooleanProperty gridVisibleProperty()
    {
        return gridVisible;
    }

    /**
     * Checks if snap-to-grid is on.
     *
     * @return {@code true} if snap-to-grid is on, {@code false} if not
     */
    public boolean isSnapToGridOn()
    {
        return snapToGrid.get();
    }

    /**
     * Sets whether snap-to-grid should be on.
     *
     * @param pSnapToGrid
     *            {@code true} if snap-to-grid should be on, {@code false} if
     *            not
     */
    public void setSnapToGrid(final boolean pSnapToGrid)
    {
        snapToGrid.set(pSnapToGrid);
    }

    /**
     * Gets the snap-to-grid property.
     *
     * @return a {@link BooleanProperty} tracking whether snap-to-grid is on or
     *         off
     */
    public BooleanProperty snapToGridProperty()
    {
        return snapToGrid;
    }

    /**
     * Gets the current grid spacing in pixels.
     *
     * @return the current grid spacing
     */
    public double getGridSpacing()
    {
        return gridSpacing.get();
    }

    /**
     * Sets the grid spacing to be used if the grid is visible and/or
     * snap-to-grid is enabled.
     *
     * <p>
     * Integer values are recommended to avoid sub-pixel positioning effects.
     * </p>
     *
     * @param pGridSpacing
     *         the grid spacing to be used
     */
    public void setGridSpacing(final double pGridSpacing)
    {
        gridSpacing.set(pGridSpacing);
    }

    /**
     * Gets the grid spacing property.
     *
     * @return the grid spacing {@link DoubleProperty}.
     */
    public DoubleProperty gridSpacingProperty()
    {
        return gridSpacing;
    }

    /**
     * Gets the read only property
     *
     * @param pType
     *         {@link EditorElement}
     * @return read only {@link BooleanProperty}
     */
    public BooleanProperty readOnlyProperty(final EditorElement pType)
    {
        Objects.requireNonNull(pType, "ElementType may not be null!");
        return readOnly.computeIfAbsent(pType, k -> new SimpleBooleanProperty());
    }

    /**
     * Returns whether or not the graph is in read only state.
     *
     * @param pType
     *         {@link EditorElement}
     * @return whether or not the graph is in read only state.
     */
    public boolean isReadOnly(final EditorElement pType)
    {
        return pType != null && readOnly.computeIfAbsent(pType, k -> new SimpleBooleanProperty()).get();
    }

    /**
     * @param pType
     *         {@link EditorElement}
     * @param pReadOnly
     *         {@code true} to set the graph editor in read only state or {@code false} (default) for edit state.
     */
    public void setReadOnly(final EditorElement pType, final boolean pReadOnly)
    {
        if (pType != null)
        {
            readOnly.computeIfAbsent(pType, k -> new SimpleBooleanProperty()).set(pReadOnly);
        }
    }

    /**
     * Additional properties that may be added and referred to in custom skin
     * implementations.
     *
     * @return a map of custom properties
     */
    public ObservableMap<String, String> getCustomProperties()
    {
        return customProperties;
    }

    @Override
    public boolean activateGesture(GraphInputGesture pGesture, Event pEvent, Object pOwner)
    {
        return eventManager.activateGesture(pGesture, pEvent, pOwner);
    }

    @Override
    public boolean finishGesture(GraphInputGesture pExpected, Object pOwner)
    {
        return eventManager.finishGesture(pExpected, pOwner);
    }
} public final class RemoveContext
{

    private final Collection<EObject> objectsToDelete = new HashSet<>();

    /**
     * Constructor
     *
     * @since 15.02.2019
     */
    public RemoveContext()
    {
        // Auto-generated constructor stub
    }

    /**
     * @param pToCheck
     *            {@link EObject} to check
     * @return {@code true} if no other involved party has created a delete
     *         command for the given object otherwise {@code false}
     * @since 15.02.2019
     */
    public boolean canRemove(final EObject pToCheck)
    {
        return objectsToDelete.add(pToCheck);
    }

    /**
     * @param pToCheck
     *            {@link EObject} to check
     * @return {@code true} any involved party has created a delete command for
     *         the given object otherwise {@code false}
     * @since 15.02.2019
     */
    public boolean contains(final EObject pToCheck)
    {
        return objectsToDelete.contains(pToCheck);
    }
}
import java.util.Collection;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.ecore.EObject;
import de.tesis.dynaware.grapheditor.GConnectionSkin;
import de.tesis.dynaware.grapheditor.GConnectorSkin;
import de.tesis.dynaware.grapheditor.GConnectorValidator;
import de.tesis.dynaware.grapheditor.GJointSkin;
import de.tesis.dynaware.grapheditor.GNodeSkin;
import de.tesis.dynaware.grapheditor.GTailSkin;
import de.tesis.dynaware.grapheditor.GraphEditor;
import de.tesis.dynaware.grapheditor.SelectionManager;
import de.tesis.dynaware.grapheditor.SkinLookup;
import de.tesis.dynaware.grapheditor.core.connections.ConnectionEventManager;
import de.tesis.dynaware.grapheditor.core.skins.GraphEditorSkinManager;
import de.tesis.dynaware.grapheditor.core.view.ConnectionLayouter;
import de.tesis.dynaware.grapheditor.core.view.GraphEditorView;
import de.tesis.dynaware.grapheditor.model.GConnection;
import de.tesis.dynaware.grapheditor.model.GConnector;
import de.tesis.dynaware.grapheditor.model.GJoint;
import de.tesis.dynaware.grapheditor.model.GModel;
import de.tesis.dynaware.grapheditor.model.GNode;
import de.tesis.dynaware.grapheditor.utils.GraphEditorProperties;
import de.tesis.dynaware.grapheditor.utils.RemoveContext;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.scene.layout.Region;
import javafx.util.Callback;
/*
 * Copyright (C) 2005 - 2014 by TESIS DYNAware GmbH
 */
package de.tesis.dynaware.grapheditor.core;





/**
 * Default implementation of the {@link GraphEditor}.
 */
public class DefaultGraphEditor implements GraphEditor
{

    private final GraphEditorProperties mProperties;
    private final GraphEditorView mView;
    private final ConnectionEventManager mConnectionEventManager = new ConnectionEventManager();
    private final GraphEditorSkinManager mSkinManager;
    private final GraphEditorController<DefaultGraphEditor> mController;

    private final ObjectProperty<GModel> mModelProperty = new ObjectPropertyBase<GModel>()
    {

        @Override
        public Object getBean()
        {
            return DefaultGraphEditor.this;
        }

        @Override
        public String getName()
        {
            return "model";
        }

    };

    /**
     * Creates a new default implementation of the {@link GraphEditor}.
     */
    public DefaultGraphEditor()
    {
        this(null);
    }

    /**
     * Creates a new default implementation of the {@link GraphEditor}.
     */
    public DefaultGraphEditor(final GraphEditorProperties pProperties)
    {
        mProperties = pProperties == null ? new GraphEditorProperties() : pProperties;
        mView = new GraphEditorView(mProperties);
        mSkinManager = new GraphEditorSkinManager(this, mView);
        mController = new GraphEditorController<>(this, mSkinManager, mView, mConnectionEventManager, mProperties);

        final ConnectionLayouter connectionLayouter = mController.getConnectionLayouter();
        mView.setConnectionLayouter(connectionLayouter);
        mSkinManager.setConnectionLayouter(connectionLayouter);
    }

    @Override
    public void setNodeSkinFactory(final Callback<GNode, GNodeSkin> pSkinFactory)
    {
        mSkinManager.setNodeSkinFactory(pSkinFactory);
    }

    @Override
    public void setConnectorSkinFactory(final Callback<GConnector, GConnectorSkin> pConnectorSkinFactory)