org.eclipse.sirius.diagram.ui.graphical.edit.policies.SpecificBorderItemSelectionEditPolicy.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.diagram.ui.graphical.edit.policies.SpecificBorderItemSelectionEditPolicy.java

Source

/******************************************************************************
 * Copyright (c) 2002, 2015 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
 *    Obeo - adaptation
 ****************************************************************************/

package org.eclipse.sirius.diagram.ui.graphical.edit.policies;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PrecisionPoint;
import org.eclipse.draw2d.geometry.PrecisionRectangle;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.EditPartViewer.Conditional;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.handles.MoveHandle;
import org.eclipse.gef.handles.ResizeHandle;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.commands.SetBoundsCommand;
import org.eclipse.gmf.runtime.diagram.ui.editparts.AbstractBorderItemEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderedShapeEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.ResizableEditPolicyEx;
import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages;
import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants;
import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator;
import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.sirius.diagram.AbstractDNode;
import org.eclipse.sirius.diagram.DDiagramElement;
import org.eclipse.sirius.diagram.business.api.query.AbstractDNodeQuery;
import org.eclipse.sirius.diagram.business.api.query.DDiagramElementQuery;
import org.eclipse.sirius.diagram.ui.business.internal.query.RequestQuery;
import org.eclipse.sirius.diagram.ui.edit.api.part.AbstractDiagramBorderNodeEditPart;
import org.eclipse.sirius.diagram.ui.edit.internal.part.PortLayoutHelper;
import org.eclipse.sirius.diagram.ui.internal.edit.commands.CenterEditPartEdgesCommand;
import org.eclipse.sirius.diagram.ui.internal.edit.commands.ChangeBendpointsOfEdgesCommand;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DNodeNameEditPart;
import org.eclipse.sirius.diagram.ui.internal.operation.ShiftEdgeIdentityAnchorOperation;
import org.eclipse.sirius.diagram.ui.tools.api.figure.locator.DBorderItemLocator;
import org.eclipse.sirius.diagram.ui.tools.api.graphical.edit.styles.IBorderItemOffsets;
import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory;
import org.eclipse.sirius.diagram.ui.tools.internal.figure.locator.FeedbackDBorderItemLocator;
import org.eclipse.sirius.diagram.ui.tools.internal.ui.NoCopyDragEditPartsTrackerEx;
import org.eclipse.sirius.ext.gmf.runtime.editparts.GraphicalHelper;

import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * The specific
 * {@link org.eclipse.gmf.runtime.diagram.ui.editpolicies.BorderItemSelectionEditPolicy}
 * .
 * 
 * @author ymortier
 * @author jdupont
 */
public class SpecificBorderItemSelectionEditPolicy extends ResizableEditPolicyEx {

    /**
     * Key for extended data of request. This key corresponds to a map that for
     * each DDiagramElement (border nodes) stores the absolute screen location
     * computed for the feedback figures.<BR>
     * This map is set during drawing of border node feedback. This map is used
     * later in the {@link SiriusContainerDropPolicy} to have the realLocation,
     * and so the real delta for each border nodes, instead of the location
     * corresponding to the move makes with the mouse. This allows to compute
     * correctly the new linked edges bendpoints to only move the last segment.
     */
    public static final String BORDER_NODE_REAL_LOCATION_KEY = "borderNodesRealLocation"; //$NON-NLS-1$

    /**
     * Key for extended data of request. This key corresponds to feedback
     * figures. This figures are added during drawing of feedback when several
     * border nodes are move simultaneously. The last moved figure is not added
     * to this list (because there will be no further feedback after this one).
     */
    private static final String BORDER_NODE_FEEDBACKS_KEY = "borderNodeFeedbacks"; //$NON-NLS-1$

    /**
     * We keep all created feedbacks to delete them at the end of the drag
     * action.
     */
    private List<IFigure> feedbacks = new ArrayList<IFigure>();

    /**
     * For each collapsed figure, we keep all computed expanded coordinates
     * until the drag action is over.
     */
    private Map<IFigure, Rectangle> correspondingExpandedCoordinate = new HashMap<IFigure, Rectangle>();

    /**
     * The edit part where feedbacks are activated.
     */
    private EditPart feedbacksActivated;

    /**
     * Status of the feedback figure. True if the feedback figure is displayed
     * (created by the showChangeBoundsFeedback(ChangeBoundsRequest) method),
     * false if not already created or deleted (with method
     * eraseChangeBoundsFeedback(ChangeBoundsRequest)).<BR>
     * This avoids to create this feedback figure in method
     * getFiguresToIgnore(ChangeBoundsRequest). <BR>
     * This was done because, there is no way to know if the feedback figure is
     * null or not (private field).
     */
    private boolean feedbackFigureDisplayed;

    private BorderNodeCollapseManager borderNodeCollapseManager;

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.gmf.runtime.diagram.ui.editpolicies.ResizableEditPolicyEx#eraseSourceFeedback(org.eclipse.gef.Request)
     */
    @Override
    public void eraseSourceFeedback(final Request request) {
        if (!(request instanceof ChangeBoundsRequest)) {
            return;
        }
        // Remove the feedback from request extended data
        getBorderNodeFeedbacks(request).remove(getDragSourceFeedbackFigure());
        if ((REQ_MOVE.equals(request.getType()) && isDragAllowed()) || REQ_CLONE.equals(request.getType())
                || REQ_ADD.equals(request.getType())) {
            eraseChangeBoundsFeedback((ChangeBoundsRequest) request);

        }
        if (RequestConstants.REQ_DROP.equals(request.getType())
                || org.eclipse.gef.RequestConstants.REQ_RESIZE.equals(request.getType())
                || org.eclipse.gef.RequestConstants.REQ_RESIZE_CHILDREN.equals(request.getType())) {
            eraseChangeBoundsFeedback((ChangeBoundsRequest) request);
            eraseChangeBoundsProhibitedFeedbackWhenDrop();
        }
    }

    @Override
    protected void eraseChangeBoundsFeedback(ChangeBoundsRequest request) {
        feedbackFigureDisplayed = false;
        super.eraseChangeBoundsFeedback(request);
    }

    /**
     * Return the list of existing feedback figures containing in the request.
     * If the request does not contains feedback figures, an empty list is
     * returned.
     * 
     * @param request
     *            The request containing the extended data.
     * @return the list of existing feedback figures contained in the request.
     */
    @SuppressWarnings("unchecked")
    private List<IFigure> getBorderNodeFeedbacks(Request request) {
        Object result = request.getExtendedData().get(BORDER_NODE_FEEDBACKS_KEY);
        if (result instanceof List<?> && Iterables.all((List<?>) result, Predicates.instanceOf(IFigure.class))) {
            return (List<IFigure>) result;
        } else {
            return new ArrayList<IFigure>();
        }
    }

    /**
     * Return a map with for each DDiagramElement the location of its feedback
     * computed during the showSourceFeedback. If the request does not contains
     * this map, an empty one is returned.
     * 
     * @param request
     *            The request containing the extended data.
     * @return map with for each DDiagramElement the location of its feedback
     */
    @SuppressWarnings("unchecked")
    private Map<DDiagramElement, Point> getLocationsForDDiagramElement(Request request) {
        Object result = request.getExtendedData().get(BORDER_NODE_REAL_LOCATION_KEY);
        if (result instanceof Map<?, ?>) {
            return (Map<DDiagramElement, Point>) result;
        } else {
            return Maps.newHashMap();
        }
    }

    private void eraseChangeBoundsProhibitedFeedbackWhenDrop() {
        for (IFigure figure : feedbacks) {
            removeFeedback(figure);
        }
        feedbacks.clear();
        correspondingExpandedCoordinate.clear();
        feedbacksActivated = null;
        if (borderNodeCollapseManager != null) {
            borderNodeCollapseManager.dispose();
        }
    }

    private BorderNodeCollapseManager getOrCreateBorderNodeCollapseManager() {
        if (borderNodeCollapseManager == null) {
            borderNodeCollapseManager = new BorderNodeCollapseManager();
        }
        return borderNodeCollapseManager;
    }

    /**
     * Calls other methods as appropriate. {@inheritDoc}
     * 
     * @see org.eclipse.gmf.runtime.diagram.ui.editpolicies.ResizableEditPolicyEx#showSourceFeedback(org.eclipse.gef.Request)
     */
    @Override
    public void showSourceFeedback(final Request request) {
        if (!(request instanceof ChangeBoundsRequest)) {
            return;
        }
        if ((REQ_MOVE.equals(request.getType()) && isDragAllowed()) || REQ_ADD.equals(request.getType())
                || REQ_CLONE.equals(request.getType())) {
            showChangeBoundsFeedback((ChangeBoundsRequest) request);
        }
        if (RequestConstants.REQ_DROP.equals(request.getType())
                || org.eclipse.gef.RequestConstants.REQ_RESIZE.equals(request.getType())
                || org.eclipse.gef.RequestConstants.REQ_RESIZE_CHILDREN.equals(request.getType())) {
            showChangeBoundsFeedback((ChangeBoundsRequest) request);
        }
    }

    /**
     * Shows or updates feedback for a change bounds request.
     * 
     * @param request
     *            the request
     */
    @SuppressWarnings("unchecked")
    @Override
    protected void showChangeBoundsFeedback(final ChangeBoundsRequest request) {
        // Get the figure of the target edit part
        EditPart hostEditPart = getHost();
        final IBorderItemEditPart borderItemEP = (IBorderItemEditPart) hostEditPart;
        EditPartViewer editPartViewer = hostEditPart.getViewer();
        Point shiftedLocation = request.getLocation().getCopy();
        EditPart targetEditPart = editPartViewer.findObjectAtExcluding(shiftedLocation, Collections.emptyList(),
                new IBorderedShapeEditPartCondition());

        if (targetEditPart instanceof AbstractGraphicalEditPart) {
            AbstractGraphicalEditPart targetAbstractGraphicalEditPart = (AbstractGraphicalEditPart) targetEditPart;
            IFigure targetFigure = targetAbstractGraphicalEditPart.getFigure();

            final IFigure feedback = getDragSourceFeedbackFigure();
            feedbackFigureDisplayed = true;
            final PrecisionRectangle rect = new PrecisionRectangle(getInitialFeedbackBounds());
            getHostFigure().translateToAbsolute(rect);
            rect.translate(request.getMoveDelta());
            rect.resize(request.getSizeDelta());
            Rectangle realLocation = null;
            // Only necessary in the case of bordered node dropping
            if (isFeedbackForBorderedNodeDropping(request, targetAbstractGraphicalEditPart)) {
                activateProhibitedFeedbacks(targetAbstractGraphicalEditPart, request);

                DBorderItemLocator borderItemLocator = new FeedbackDBorderItemLocator(targetFigure);
                if (getOrCreateBorderNodeCollapseManager().isCollapsed(borderItemEP)) {
                    borderItemLocator.setBorderItemOffset(IBorderItemOffsets.COLLAPSE_FILTER_OFFSET);
                } else {
                    borderItemLocator.setBorderItemOffset(IBorderItemOffsets.DEFAULT_OFFSET);
                }
                // Verify if the dropping of bordered node is not an element of
                // the same level
                if (targetAbstractGraphicalEditPart.getParent() != getHost().getParent().getParent()) {
                    // Verify if the parent is the diagram. If it, calculates
                    // the position of the ghost relative to the diagram,
                    // otherwise compared to the parent
                    if (targetAbstractGraphicalEditPart.getParent() instanceof DiagramEditPart) {
                        targetAbstractGraphicalEditPart.getFigure().translateToAbsolute(rect);
                    } else {
                        targetAbstractGraphicalEditPart.getFigure().translateToRelative(rect);
                    }
                } else {
                    getHostFigure().translateToRelative(rect);
                }
                // if the bordered node is collapsed, we extend the feedback to
                // consider his extended bounds
                if (hostEditPart instanceof IGraphicalEditPart
                        && getOrCreateBorderNodeCollapseManager().isCollapsed((IGraphicalEditPart) hostEditPart)) {
                    Dimension initialDim = getOrCreateBorderNodeCollapseManager()
                            .getInitialDimension((IGraphicalEditPart) hostEditPart);
                    Rectangle newBoundsAbsolute = PortLayoutHelper.getUncollapseCandidateLocation(initialDim, rect,
                            null);
                    borderItemLocator.setConstraint(newBoundsAbsolute);
                    borderItemLocator.setBorderItemOffset(IBorderItemOffsets.DEFAULT_OFFSET);
                    rect.setBounds(newBoundsAbsolute);
                }
                realLocation = borderItemLocator.getValidLocation(rect, feedback, getFiguresToIgnore(request),
                        getBorderNodeFeedbacks(request));

                targetFigure.translateToAbsolute(realLocation);
                feedback.translateToRelative(realLocation);
                feedback.setBounds(realLocation);
                storeFeedback(feedback, request);

                // Add the real location in request (this location is used in
                // SiriusContainerDropPolicy)
                Map<DDiagramElement, Point> locationsForDDiagramElement = getLocationsForDDiagramElement(request);
                if (getOrCreateBorderNodeCollapseManager().isCollapsed(borderItemEP)) {
                    realLocation = PortLayoutHelper.getCollapseCandidateLocation(borderItemEP.getFigure().getSize(),
                            realLocation, targetFigure.getBounds());
                }
                locationsForDDiagramElement.put((DDiagramElement) borderItemEP.resolveSemanticElement(),
                        realLocation.getLocation());
                request.getExtendedData().put(BORDER_NODE_REAL_LOCATION_KEY, locationsForDDiagramElement);
            } else {
                activateProhibitedFeedbacks(hostEditPart.getParent(), request);
                final IBorderItemLocator borderItemLocator = borderItemEP.getBorderItemLocator();

                if (borderItemLocator != null) {

                    getHostFigure().translateToRelative(rect);
                    Rectangle newRect = getOrCreateBorderNodeCollapseManager()
                            .expandCollapsedNodeBounds(borderItemEP, rect);
                    if (newRect != null) {
                        rect.setBounds(newRect);
                    }
                    if (borderItemLocator instanceof DBorderItemLocator) {
                        // Compute the list of figures to ignore during the
                        // conflict detection.
                        List<IFigure> figuresToIgnore = getFiguresToIgnore(request);
                        realLocation = ((DBorderItemLocator) borderItemLocator).getValidLocation(rect,
                                borderItemEP.getFigure(), figuresToIgnore, getBorderNodeFeedbacks(request));
                    } else {
                        realLocation = borderItemLocator.getValidLocation(rect.getCopy(), borderItemEP.getFigure());
                    }
                    if (getOrCreateBorderNodeCollapseManager().hasBeenExpanded()) {
                        getOrCreateBorderNodeCollapseManager().restoreCollapsedNode(borderItemEP);
                    }
                    getHostFigure().translateToAbsolute(realLocation);

                    feedback.translateToRelative(realLocation);
                    feedback.setBounds(realLocation);
                    storeFeedback(feedback, request);
                }
            }
        }
    }

    /**
     * The feedback is stored only if the request corresponds to several
     * elements and that the current element is not the last.
     * 
     * @param feedback
     *            The figure to store.
     * @param request
     *            The request to store in.
     */
    @SuppressWarnings("unchecked")
    private void storeFeedback(IFigure feedback, ChangeBoundsRequest request) {
        if (request.getEditParts().size() > 1
                && !getHost().equals(request.getEditParts().get(request.getEditParts().size() - 1))) {
            // Store the feedback new location in request to use it for
            // other feedback conflict detection
            List<IFigure> borderNodeFeedbacks = getBorderNodeFeedbacks(request);
            int currentIndex = borderNodeFeedbacks.indexOf(feedback);
            if (currentIndex != -1) {
                borderNodeFeedbacks.set(currentIndex, feedback);
            } else {
                borderNodeFeedbacks.add(feedback);
            }
            request.getExtendedData().put(BORDER_NODE_FEEDBACKS_KEY, borderNodeFeedbacks);
        }
    }

    /**
     * Activates feedbacks for all collapsed node to avoid to place an other
     * node in their forbidden area.
     * 
     * @param hostEditPart
     *            the edit part hosting the figure we are moving.
     * @param targetEditPart
     *            the container target edit part where the node is moving (can
     *            be the drop target edit part or the current node container
     *            edit part)
     * @param request
     *            the change bounds request.
     */
    private void activateProhibitedFeedbacks(EditPart targetEditPart, ChangeBoundsRequest request) {

        // if prohibited feedbacks were activated for an other edit part, we
        // clean them.
        if (isFeedbacksActivated() && !isFeedbacksActivatedForEditPart(targetEditPart)) {
            eraseChangeBoundsProhibitedFeedbackWhenDrop();
        }
        for (Object child : targetEditPart.getChildren()) {
            if (!request.getEditParts().contains(child) && child instanceof AbstractDiagramBorderNodeEditPart) {
                AbstractDiagramBorderNodeEditPart borderNodeEditPart = (AbstractDiagramBorderNodeEditPart) child;
                if (getOrCreateBorderNodeCollapseManager().isCollapsed(borderNodeEditPart)) {
                    configureFeedback(borderNodeEditPart, getBorderNodeFeedbacks(request));
                }
            }
        }
        feedbacksActivated = targetEditPart;
    }

    /**
     * Returns whether the prohibited feedbacks are activated for the given
     * target edit part.
     * 
     * @param targetEditPart
     *            the edit part we want to know if the prohibited feedbacks are
     *            activated.
     * @return true if activated otherwise false
     */
    private boolean isFeedbacksActivatedForEditPart(EditPart targetEditPart) {
        return feedbacksActivated == targetEditPart;
    }

    /**
     * Returns whether the prohibited feedbacks are activated somewhere
     * 
     * @return true if activated otherwise false
     */
    private boolean isFeedbacksActivated() {
        return feedbacksActivated != null;
    }

    /**
     * configure the prohibited feedback and area for the given border node edit
     * part.
     * 
     * @param borderNodeEditPart
     *            the edit part hosting the collapsed node figure.
     * @param otherFeedbackFigures
     *            In case of simultaneous moves, this list corresponds to the
     *            already known border nodes after move (generally the feedback
     *            figure)
     */
    private void configureFeedback(AbstractBorderItemEditPart borderNodeEditPart,
            List<IFigure> otherFeedbackFigures) {

        IFigure figure = borderNodeEditPart.getFigure();
        EditPart parentEditPart = borderNodeEditPart.getParent();
        // we don't create feedbacks if there are already activated.
        if (!isFeedbacksActivatedForEditPart(borderNodeEditPart.getParent())) {
            Dimension initialDim = getOrCreateBorderNodeCollapseManager().getInitialDimension(borderNodeEditPart);
            Rectangle bounds = figure.getBounds().getCopy();
            Rectangle parentBounds = null;
            if (parentEditPart instanceof IGraphicalEditPart) {
                IFigure parentFigure = ((IGraphicalEditPart) parentEditPart).getFigure();
                parentBounds = parentFigure.getBounds().getCopy();
                if (parentFigure instanceof NodeFigure) {
                    parentBounds = ((NodeFigure) parentFigure).getHandleBounds().getCopy();
                }
            }
            Rectangle newBounds = PortLayoutHelper.getUncollapseCandidateLocation(initialDim, bounds, parentBounds);
            final IBorderItemLocator borderItemLocator = borderNodeEditPart.getBorderItemLocator();
            // get real location from DBorderItemLocator
            Rectangle realNewBounds = getRealExpandedBounds(figure, newBounds, borderItemLocator,
                    otherFeedbackFigures);

            PrecisionRectangle precisionRectangle = new PrecisionRectangle(realNewBounds);
            // // Use a ghost rectangle for feedback
            RectangleFigure r = new RectangleFigure();
            r.setLineStyle(Graphics.LINE_DOT);
            r.setBounds(precisionRectangle);

            addFeedback(r);
            figure.translateToAbsolute(precisionRectangle);
            r.translateToRelative(precisionRectangle);
            r.setBounds(precisionRectangle);

            r.setBackgroundColor(ColorConstants.red);
            r.setAlpha(70);

            feedbacks.add(r);
            correspondingExpandedCoordinate.put(figure, realNewBounds);
        }
        Rectangle figureNewBounds = correspondingExpandedCoordinate.get(figure);
        if (figureNewBounds != null) {

            // we update the figure bounds to be considered by the border item
            // locator. Because we don't set the layout constraints, the figure
            // bounds will come back to their original collapsed bounds at
            // refresh
            // time.
            figure.setBounds(figureNewBounds);
        }
    }

    /**
     * Provides the location that should be if the node was not collapsed.
     * 
     * @param figure
     *            the current node figure.
     * @param candidateNewBounds
     *            the new bounds candidates.
     * @param borderItemLocator
     *            the figure edit part border item locator.
     * @param otherFeedbackFigures
     *            In case of simultaneous moves, this list corresponds to the
     *            already known border nodes after move (generally the feedback
     *            figure)
     * @return the real location from the border item locator.
     */
    private Rectangle getRealExpandedBounds(IFigure figure, Rectangle candidateNewBounds,
            final IBorderItemLocator borderItemLocator, List<IFigure> otherFeedbackFigures) {
        Rectangle realNewBounds = candidateNewBounds;
        if (borderItemLocator instanceof DBorderItemLocator) {
            Rectangle oldConstraint = ((DBorderItemLocator) borderItemLocator).getCurrentConstraint();
            borderItemLocator.setConstraint(candidateNewBounds);

            Dimension oldOffset = ((DBorderItemLocator) borderItemLocator).getBorderItemOffset();
            ((DBorderItemLocator) borderItemLocator).setBorderItemOffset(IBorderItemOffsets.DEFAULT_OFFSET);

            realNewBounds = ((DBorderItemLocator) borderItemLocator).getValidLocation(candidateNewBounds, figure,
                    Collections.singleton(figure), otherFeedbackFigures);

            ((DBorderItemLocator) borderItemLocator).setBorderItemOffset(oldOffset);
            borderItemLocator.setConstraint(oldConstraint);
            // The setConstraint has been reset to old value. The
            // borderItemMovedState must be reset to avoid that
            // borderItemLocator considers it as a real change.
            ((DBorderItemLocator) borderItemLocator).resetBorderItemMovedState();

        }
        return realNewBounds;
    }

    /**
     * Tell if according to the <code>request</code>'s location we must provide
     * a feedback for a drop of borderedNode to another parent or a feedback for
     * a simple move of borderedNode without changing visually the parent.
     * 
     * @param request
     *            the {@link ChangeBoundsRequest} providing the location of the
     *            mouse
     * 
     * @param targetAbstractGraphicalEditPart
     *            the target EditPart on which to provide the feedback
     * 
     * @return true if we must provide a feedback for a drop
     */
    private boolean isFeedbackForBorderedNodeDropping(ChangeBoundsRequest request,
            AbstractGraphicalEditPart targetAbstractGraphicalEditPart) {
        boolean isFeedbackForBorderedNodeDropping = false;
        if (!(new RequestQuery(request).isResize() || RequestConstants.REQ_RESIZE_CHILDREN.equals(request.getType())
                || RequestConstants.REQ_MOVE.equals(request.getType()))) {

            EditPart hostEditPart = getHost();
            IFigure diagramFigure = ((AbstractGraphicalEditPart) hostEditPart.getRoot().getChildren().get(0))
                    .getFigure();

            IFigure targetFigure = targetAbstractGraphicalEditPart.getFigure();

            // verify not case of a label and not on the diagram to the ghost
            // appears only on the nodes and not on the diagram during a drop
            if (targetFigure != diagramFigure && !(hostEditPart instanceof DNodeNameEditPart)) {
                // Necessary for sequence diagrams, for the feedback does not
                // shift
                isFeedbackForBorderedNodeDropping = targetAbstractGraphicalEditPart != hostEditPart
                        && targetAbstractGraphicalEditPart != hostEditPart.getParent();
            }
        }
        return isFeedbackForBorderedNodeDropping;
    }

    @Override
    protected Command getResizeCommand(ChangeBoundsRequest request) {
        EditPart host = getHost();

        if (host instanceof IBorderItemEditPart) {
            final IBorderItemEditPart borderItemEP = (IBorderItemEditPart) host;
            Rectangle newBounds = getNewBounds(request);

            SetBoundsCommand setBoundsCommand = new SetBoundsCommand(borderItemEP.getEditingDomain(),
                    DiagramUIMessages.SetLocationCommand_Label_Resize,
                    new EObjectAdapter((View) getHost().getModel()), newBounds);

            TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) borderItemEP).getEditingDomain();
            CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(editingDomain,
                    setBoundsCommand.getLabel());
            ctc.add(setBoundsCommand);

            // Compute the shift delta according to the real future bounds.
            PrecisionPoint delta = new PrecisionPoint();
            if (newBounds != null) {
                if (getHost() instanceof IGraphicalEditPart) {
                    final Point parentOrigin = borderItemEP.getFigure().getParent().getBounds().getTopLeft();
                    Point oldRelativeBounds = borderItemEP.getFigure().getBounds().getTopLeft()
                            .translate(parentOrigin.getNegated());
                    delta = new PrecisionPoint(newBounds.getLocation().translate(oldRelativeBounds.getNegated()));
                }
            }

            ShiftEdgeIdentityAnchorOperation operation = new ShiftEdgeIdentityAnchorOperation(request,
                    newBounds.getSize(), delta);
            ICommand command = CommandFactory.createICommand(editingDomain, operation);
            ctc.add(command);

            // we add a command to keep the edges centered (if they should be)
            CenterEditPartEdgesCommand centerEditPartEdgesCommand = new CenterEditPartEdgesCommand(borderItemEP,
                    request);
            ctc.add(centerEditPartEdgesCommand);
            return new ICommandProxy(ctc);
        }
        return super.getResizeCommand(request);
    }

    /**
     * Returns the command contribution to a change bounds request.
     * 
     * @param request
     *            the change bounds request
     * @return the command contribution to the request
     */
    @Override
    protected Command getMoveCommand(final ChangeBoundsRequest request) {

        final IBorderItemEditPart borderItemEP = (IBorderItemEditPart) getHost();
        Rectangle newBounds = getNewBounds(request);
        if (newBounds != null) {
            final ICommand moveCommand = new SetBoundsCommand(borderItemEP.getEditingDomain(),
                    DiagramUIMessages.Commands_MoveElement, new EObjectAdapter((View) getHost().getModel()),
                    newBounds.getLocation());
            Command result = new ICommandProxy(moveCommand);

            if (getHost() instanceof IGraphicalEditPart) {
                final Point parentOrigin = borderItemEP.getFigure().getParent().getBounds().getTopLeft();
                Point oldRelativeBounds = borderItemEP.getFigure().getBounds().getTopLeft()
                        .translate(parentOrigin.getNegated());
                PrecisionPoint delta = new PrecisionPoint(
                        newBounds.getLocation().translate(oldRelativeBounds.getNegated()));
                GraphicalHelper.applyZoomOnPoint((IGraphicalEditPart) getHost(), delta);

                IGraphicalEditPart hostPart = (IGraphicalEditPart) getHost();
                CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(hostPart.getEditingDomain(),
                        result.getLabel());
                ctc.add(new CommandProxy(result));
                ctc.add(new ChangeBendpointsOfEdgesCommand(hostPart, delta));
                result = new ICommandProxy(ctc);
            }
            return result;
        }
        return null;
    }

    /**
     * Computes the new bounds of the border item.
     * 
     * @param request
     *            the change bounds request.
     * @return the new bounds according to the request and the border item
     *         locator.
     */
    private Rectangle getNewBounds(final ChangeBoundsRequest request) {
        IBorderItemEditPart borderItemEP = (IBorderItemEditPart) getHost();
        IBorderItemLocator borderItemLocator = borderItemEP.getBorderItemLocator();
        if (borderItemLocator != null) {
            final PrecisionRectangle rect = new PrecisionRectangle(getInitialFeedbackBounds());
            getHostFigure().translateToAbsolute(rect);
            rect.translate(request.getMoveDelta());
            rect.resize(request.getSizeDelta());

            getHostFigure().translateToRelative(rect);

            Rectangle realLocation;
            if (borderItemLocator instanceof DBorderItemLocator) {
                // Compute the list of figures to ignore during the conflict
                // detection.
                List<IFigure> figuresToIgnore = getFiguresToIgnore(request);

                // if the bordered node is collapsed, we compute his expanded
                // bounds
                Rectangle newBounds = getOrCreateBorderNodeCollapseManager().expandCollapsedNodeBounds(borderItemEP,
                        rect);
                if (newBounds != null) {
                    rect.setBounds(newBounds);
                }

                realLocation = ((DBorderItemLocator) borderItemLocator).getValidLocation(rect,
                        borderItemEP.getFigure(), figuresToIgnore, getBorderNodeFeedbacks(request));
                if (getOrCreateBorderNodeCollapseManager().hasBeenExpanded()) {
                    getOrCreateBorderNodeCollapseManager().restoreCollapsedNode(borderItemEP);
                    IFigure parentFigure = ((DBorderItemLocator) borderItemLocator).getParentFigure();
                    Rectangle parentBounds = parentFigure.getBounds().getCopy();
                    if (parentFigure instanceof NodeFigure) {
                        parentBounds = ((NodeFigure) parentFigure).getHandleBounds().getCopy();
                    }
                    Rectangle collapsedBounds = PortLayoutHelper.getCollapseCandidateLocation(
                            getOrCreateBorderNodeCollapseManager().getCollapsedRectangle().getSize(), realLocation,
                            parentBounds);
                    realLocation.setBounds(collapsedBounds);
                }
                ((DBorderItemLocator) borderItemLocator).setFiguresToIgnoresDuringNextRelocate(figuresToIgnore);
            } else {
                realLocation = borderItemLocator.getValidLocation(rect.getCopy(), borderItemEP.getFigure());
            }

            final Point parentOrigin = borderItemEP.getFigure().getParent().getBounds().getTopLeft();
            final Dimension d = realLocation.getTopLeft().getDifference(parentOrigin);
            final Point location = new Point(d.width, d.height);
            return new Rectangle(location, rect.getSize());

        }
        return null;
    }

    /**
     * Return a list of all figure that will be moved by this
     * ChangeBoundsRequest (the figure of the editParts).
     * 
     * @param request
     *            The request created by the edit part that have been moved.
     * @return list of all figure that will be moved
     */
    protected List<IFigure> getFiguresToIgnore(final ChangeBoundsRequest request) {
        List<IFigure> figuresToIgnore = Lists.newArrayList();
        for (Object part : request.getEditParts()) {
            if (part instanceof org.eclipse.gef.GraphicalEditPart) {
                figuresToIgnore.add(((org.eclipse.gef.GraphicalEditPart) part).getFigure());
            }
        }
        // Add the feedback figure only if it was created before (by a drag for
        // example).
        if (feedbackFigureDisplayed) {
            figuresToIgnore.add(getDragSourceFeedbackFigure());
        }
        return figuresToIgnore;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.gmf.runtime.diagram.ui.editpolicies.ResizableEditPolicyEx#addSelectionHandles()
     */
    @Override
    protected void addSelectionHandles() {
        super.addSelectionHandles();
        boolean mustMoveCursorBeDisabled = mustMoveCursorBeDisabled();

        for (Object handle : handles) {
            if (mustMoveCursorBeDisabled) {
                if (handle instanceof MoveHandle || handle instanceof ResizeHandle) {
                    ((IFigure) handle).setCursor(null);
                }
            }
            if (handle instanceof MoveHandle) {
                // We set a drag tracker that will not cause the
                // duplication of graphical elements when holding "Ctrl" key
                // down and moving an element
                ((MoveHandle) handle).setDragTracker(new NoCopyDragEditPartsTrackerEx(getHost()));
            }

        }
    }

    /**
     * Check if the host corresponds to a borederdNode that is collapsed. In
     * this case, the move cursor must be disabled.
     * 
     * @return true if the move cursor must be disabled, false otherwise.
     */
    protected boolean mustMoveCursorBeDisabled() {
        boolean result = false;
        if (getHost() instanceof GraphicalEditPart) {
            EObject semantic = ((GraphicalEditPart) getHost()).resolveSemanticElement();
            if (semantic instanceof AbstractDNode) {
                result = new AbstractDNodeQuery((AbstractDNode) semantic).isBorderedNode()
                        && new DDiagramElementQuery((AbstractDNode) semantic).isCollapsed();
            }
        }
        return result;
    }

    static class IBorderedShapeEditPartCondition implements Conditional {

        @Override
        public boolean evaluate(EditPart editpart) {
            return editpart instanceof IBorderedShapeEditPart;
        }

    }
}