org.eclipse.sirius.diagram.ui.tools.internal.layout.provider.PinnedElementsLayoutProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.diagram.ui.tools.internal.layout.provider.PinnedElementsLayoutProvider.java

Source

/*******************************************************************************
 * Copyright (c) 2009 THALES GLOBAL SERVICES.
 * 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:
 *    Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.sirius.diagram.tools.api.layout.PinHelper;
import org.eclipse.sirius.diagram.ui.edit.api.part.AbstractDiagramElementContainerEditPart;
import org.eclipse.sirius.diagram.ui.edit.api.part.IDiagramBorderNodeEditPart;
import org.eclipse.sirius.diagram.ui.edit.api.part.IDiagramContainerEditPart;
import org.eclipse.sirius.diagram.ui.edit.api.part.IDiagramElementEditPart;
import org.eclipse.sirius.diagram.ui.edit.api.part.IDiagramListEditPart;
import org.eclipse.sirius.diagram.ui.edit.api.part.IDiagramNameEditPart;
import org.eclipse.sirius.diagram.ui.edit.api.part.IDiagramNodeEditPart;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DNodeContainerViewNodeContainerCompartment2EditPart;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DNodeContainerViewNodeContainerCompartmentEditPart;
import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.DefaultLayoutProvider;
import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.ExtendableLayoutProvider;
import org.eclipse.sirius.diagram.ui.tools.internal.layout.ArrangeAllWithAutoSize;
import org.eclipse.sirius.diagram.ui.tools.internal.layout.PinnedElementsHandler;

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

/**
 * A layout provider which handles pinned elements after an initial layout.
 * 
 * @author pcdavid
 */
public class PinnedElementsLayoutProvider extends DefaultLayoutProvider {
    /**
     * The layout provider which was executed before us. Needed to obtain the
     * new
     */
    private final ExtendableLayoutProvider baseProvider;

    private Predicate<Object> validateAllElementInArrayListAreIDiagramElementEditPart = new Predicate<Object>() {

        public boolean apply(Object input) {
            return input instanceof IDiagramElementEditPart;
        }
    };

    /**
     * Creates a <code>FixOverlapsProvider</code>.
     * 
     * @param baseProvider
     *            the layout provider which computed the initial locations.
     */
    public PinnedElementsLayoutProvider(final ExtendableLayoutProvider baseProvider) {
        this.baseProvider = baseProvider;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Command layoutEditParts(final List selectedObjects, final IAdaptable layoutHint) {
        final Map<IGraphicalEditPart, Rectangle> initialBounds = Maps
                .newHashMap(baseProvider.getExtender().getUpdatedBounds());

        // Finds if there are unpinned diagram elements to keep fixed stored in
        // the LayoutHint as a Collection
        ArrayList<IDiagramElementEditPart> elementsToKeepFixed = Lists.newArrayList();
        if (layoutHint.getAdapter(Collection.class) instanceof ArrayList<?>
                && Iterables.all((ArrayList<?>) layoutHint.getAdapter(Collection.class),
                        validateAllElementInArrayListAreIDiagramElementEditPart)) {
            elementsToKeepFixed = (ArrayList<IDiagramElementEditPart>) layoutHint.getAdapter(Collection.class);
        }

        if (ArrangeAllWithAutoSize.isEnabled()) {
            adjustAutoSizedContainers(initialBounds, elementsToKeepFixed);
        }
        final Collection<IGraphicalEditPart> editParts = Lists
                .newArrayList(Iterables.filter(selectedObjects, IGraphicalEditPart.class));
        // Removes the connectionEditPart
        editParts.removeAll(Lists.newArrayList(Iterables.filter(selectedObjects, ConnectionEditPart.class)));
        final CompoundCommand cc = new CompoundCommand();

        handlePinnedElements(editParts, initialBounds, cc, elementsToKeepFixed);
        return cc.unwrap();
    }

    private void adjustAutoSizedContainers(final Map<IGraphicalEditPart, Rectangle> initialBounds,
            ArrayList<IDiagramElementEditPart> elementstToKeepFixed) {
        PinHelper pinHelper = new PinHelper();
        for (Entry<IGraphicalEditPart, Rectangle> entry : initialBounds.entrySet()) {
            if (entry.getKey() instanceof AbstractDiagramElementContainerEditPart) {
                final AbstractDiagramElementContainerEditPart container = (AbstractDiagramElementContainerEditPart) entry
                        .getKey();
                if (!pinHelper.isPinned(container.resolveDiagramElement())
                        && !elementstToKeepFixed.contains(container)) {
                    final Rectangle bounds = entry.getValue().getCopy();
                    final Rectangle autoSizedBounds = container.getAutosizedDimensions();
                    bounds.width = autoSizedBounds.width;
                    bounds.height = autoSizedBounds.height;
                    entry.setValue(bounds);
                }
            }
        }
    }

    private void handlePinnedElements(final Collection<IGraphicalEditPart> editParts,
            final Map<IGraphicalEditPart, Rectangle> initialBounds, final CompoundCommand cmd,
            ArrayList<IDiagramElementEditPart> elementstToKeepFixed) {
        /*
         * Depth-first recursion on containers.
         */
        for (IGraphicalEditPart part : editParts) {
            final Collection<IGraphicalEditPart> children = getChildrenOfInterest(part);
            if (!children.isEmpty()) {
                handlePinnedElements(children, initialBounds, cmd, elementstToKeepFixed);
            }
        }
        /*
         * Base case: handle pinned elements at this particular level.
         */
        final Map<IGraphicalEditPart, Rectangle> initialBoundsForThisLevel = Maps.filterEntries(initialBounds,
                new Predicate<Map.Entry<IGraphicalEditPart, Rectangle>>() {
                    public boolean apply(final Entry<IGraphicalEditPart, Rectangle> input) {
                        return editParts.contains(input.getKey());
                    }
                });
        final PinnedElementsHandler handler = new PinnedElementsHandler(editParts, initialBoundsForThisLevel,
                elementstToKeepFixed);
        final Map<IGraphicalEditPart, Point> newPositions = handler.computeSolution();
        for (Entry<IGraphicalEditPart, Point> entry : newPositions.entrySet()) {
            final IGraphicalEditPart part = entry.getKey();
            final Point position = entry.getValue();
            final Command cbc = createChangeBoundsCommand(part, position);
            cmd.add(cbc);
        }
    }

    /**
     * Finds the "real" children of the specified edit part that needs to be
     * laid out.
     */
    private Collection<IGraphicalEditPart> getChildrenOfInterest(final IGraphicalEditPart gep) {
        final Iterable<IGraphicalEditPart> rawChildren = Iterables.filter(gep.getChildren(),
                IGraphicalEditPart.class);
        // Ignore these, which are technically children edit parts but not
        // "inside" the container.
        final Predicate<Object> invalidChildKind = Predicates.or(
                Predicates.instanceOf(IDiagramBorderNodeEditPart.class),
                Predicates.instanceOf(IDiagramNameEditPart.class));
        // These are OK.
        final Predicate<Object> validChildKind = Predicates.or(Predicates.instanceOf(IDiagramNodeEditPart.class),
                Predicates.instanceOf(IDiagramContainerEditPart.class),
                Predicates.instanceOf(IDiagramListEditPart.class));
        final Predicate<Object> isProperChild = Predicates.and(validChildKind, Predicates.not(invalidChildKind));
        final Collection<IGraphicalEditPart> result = Lists
                .newArrayList(Iterables.filter(rawChildren, isProperChild));
        // Containers have an intermediate level of children edit parts. We
        // ignore these "wrapper" parts, but must look inside for proper
        // children of the container.
        for (IGraphicalEditPart part : Iterables.filter(rawChildren, Predicates.not(isProperChild))) {
            if (part instanceof DNodeContainerViewNodeContainerCompartmentEditPart
                    || part instanceof DNodeContainerViewNodeContainerCompartment2EditPart) {
                result.addAll(getChildrenOfInterest(part));
            }
        }
        return result;
    }

    /**
     * Create the command that changes the bounds of the specified edit part.
     * 
     * @param editPart
     *            the specified edit part
     * 
     * @param newPosition
     *            the new position of the figure.
     * @return the command that changes the bounds of the specified edit part.
     */
    protected Command createChangeBoundsCommand(final IGraphicalEditPart editPart, final Point newPosition) {
        Command result = null;
        final Object existingRequest = this.findRequest(editPart, org.eclipse.gef.RequestConstants.REQ_MOVE);
        ChangeBoundsRequest request = null;
        double scale = 1.0;
        if (editPart.getRoot() instanceof DiagramRootEditPart) {
            final ZoomManager zoomManager = ((DiagramRootEditPart) editPart.getRoot()).getZoomManager();
            scale = zoomManager.getZoom();
        }
        if (existingRequest instanceof ChangeBoundsRequest) {
            request = (ChangeBoundsRequest) existingRequest;
        } else if (existingRequest == null) {
            request = new ChangeBoundsRequest();
            request.setEditParts(editPart);
            result = this.buildCommandWrapper(request, editPart);
        }
        if (newPosition != null) {
            final Dimension delta = newPosition.getDifference(editPart.getFigure().getBounds().getLocation());
            delta.width *= scale;
            delta.height *= scale;
            if (request != null) {
                request.setMoveDelta(new Point(delta.width, delta.height));
                request.setLocation(newPosition);
                request.setType(org.eclipse.gef.RequestConstants.REQ_MOVE);
            } else {
                // no move, return null.
                return null;
            }
        }
        return result;
    }
}