org.eclipse.sirius.diagram.ui.tools.internal.actions.distribute.DistributeAction.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.diagram.ui.tools.internal.actions.distribute.DistributeAction.java

Source

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

import java.util.Collections;
import java.util.List;

import org.eclipse.draw2d.PositionConstants;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.tools.ToolUtilities;
import org.eclipse.gmf.runtime.common.core.util.StringStatics;
import org.eclipse.gmf.runtime.diagram.ui.actions.DiagramAction;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.sirius.diagram.ui.provider.DiagramUIPlugin;
import org.eclipse.sirius.diagram.ui.provider.Messages;
import org.eclipse.sirius.diagram.ui.tools.api.image.DiagramImagesPath;
import org.eclipse.sirius.diagram.ui.tools.api.requests.DistributeRequest;
import org.eclipse.sirius.diagram.ui.tools.api.ui.actions.ActionIds;
import org.eclipse.sirius.diagram.ui.tools.internal.util.EditPartQuery;
import org.eclipse.ui.IWorkbenchPage;

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

/**
 * A {@link DiagramAction} to distribute shapes.<BR>
 * Inspired by the class
 * {@link org.eclipse.gmf.runtime.diagram.ui.actions.internal.ArrangeAction}.
 * 
 * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a>
 */
@SuppressWarnings("restriction")
public class DistributeAction extends DiagramAction {

    /** Constant indicating horizontal distribution with uniform gaps. */
    public static final int GAPS_HORIZONTALLY = 0;

    /** Constant indicating distribution of centers horizontally. */
    public static final int CENTERS_HORIZONTALLY = 1;

    /** Constant indicating vertical distribution with uniform gaps. */
    public static final int GAPS_VERTICALLY = 2;

    /** Constant indicating distribution of centers vertically. */
    public static final int CENTERS_VERTICALLY = 3;

    /**
     * The distribution type must by one of:
     * <UL>
     * <LI>DistributeAction.HORIZONTALLY_WITH_UNIFORM_GAPS</LI>
     * <LI>DistributeAction.CENTERS_HORIZONTALLY</LI>
     * <LI>DistributeAction.VERTICALLY_WITH_UNIFORM_GAPS</LI>
     * <LI>DistributeAction.CENTERS_VERTICALLY</LI>
     * </UL>
     */
    private int distributeType;

    /**
     * Constructs a DistributeAction with the given part and distribute type.
     * 
     * The distribute type must by one of:
     * <UL>
     * <LI>DistributeAction.HORIZONTALLY_WITH_UNIFORM_GAPS</LI>
     * <LI>DistributeAction.CENTERS_HORIZONTALLY</LI>
     * <LI>DistributeAction.VERTICALLY_WITH_UNIFORM_GAPS</LI>
     * <LI>DistributeAction.CENTERS_VERTICALLY</LI>
     * </UL>
     * 
     * @param workbenchPage
     *            the workbench part used to obtain context
     * @param distributeType
     *            The distribute type
     * @param isToolbarItem
     *            the indicator of whether or not this is a toolbar action as
     *            opposed to a context-menu action.
     */
    protected DistributeAction(IWorkbenchPage workbenchPage, int distributeType, boolean isToolbarItem) {
        super(workbenchPage);
        this.distributeType = distributeType;
        setText(getLabel(distributeType, isToolbarItem));
        setToolTipText(getTooltip(distributeType));
    }

    /**
     * Get the label of the action according to <code>distributionType</code>
     * and <code>isToolbarItem</code>.
     * 
     * @param distributeType
     *            the kind of distribution.
     * @param isToolbarItem
     *            the indicator of whether or not this is a toolbar action as
     *            opposed to a context-menu action
     * @return The label
     */
    public static String getLabel(int distributeType, boolean isToolbarItem) {
        String label = StringStatics.BLANK;
        switch (distributeType) {
        case DistributeAction.GAPS_HORIZONTALLY:
            label = Messages.DistributeAction_gapsHorizontallyLabel;
            break;
        case DistributeAction.CENTERS_HORIZONTALLY:
            label = Messages.DistributeAction_centersHorizontallyLabel;
            break;
        case DistributeAction.GAPS_VERTICALLY:
            label = Messages.DistributeAction_gapsVerticallyLabel;
            break;
        case DistributeAction.CENTERS_VERTICALLY:
            label = Messages.DistributeAction_centersVerticallyLabel;
            break;
        default:
            break;
        }
        if (isToolbarItem && !StringStatics.BLANK.equals(label)) {
            // Use same label but with distribute prefix
            switch (distributeType) {
            case DistributeAction.GAPS_HORIZONTALLY:
                label = Messages.DistributeAction_distributeGapsHorizontallyLabel;
                break;
            case DistributeAction.CENTERS_HORIZONTALLY:
                label = Messages.DistributeAction_distributeCentersHorizontallyLabel;
                break;
            case DistributeAction.GAPS_VERTICALLY:
                label = Messages.DistributeAction_distributeGapsVerticallyLabel;
                break;
            case DistributeAction.CENTERS_VERTICALLY:
                label = Messages.DistributeAction_distributeCentersVerticallyLabel;
                break;
            default:
                break;
            }
        }
        return label;
    }

    /**
     * Get the tooltip of the action according to <code>distributionType</code>.
     * 
     * @param distributeType
     *            the kind of distribution.
     * @return The tooltip
     */
    public static String getTooltip(int distributeType) {
        String tooltip = StringStatics.BLANK;
        switch (distributeType) {
        case DistributeAction.GAPS_HORIZONTALLY:
            tooltip = Messages.DistributeAction_distributeGapsHorizontallyTooltip;
            break;
        case DistributeAction.CENTERS_HORIZONTALLY:
            tooltip = Messages.DistributeAction_distributeCentersHorizontallyTooltip;
            break;
        case DistributeAction.GAPS_VERTICALLY:
            tooltip = Messages.DistributeAction_distributeGapsVerticallyTooltip;
            break;
        case DistributeAction.CENTERS_VERTICALLY:
            tooltip = Messages.DistributeAction_distributeCentersVerticallyTooltip;
            break;
        default:
            break;
        }
        return tooltip;
    }

    /**
     * Creates the Distribute action to distribute shapes horizontally with
     * uniform gaps.
     * 
     * @param workbenchPage
     *            the workbench part used to obtain context
     * @param isToolbarItem
     *            the indicator of whether or not this is a toolbar action as
     *            opposed to a context-menu action.
     * @return the corresponding action
     */
    public static DistributeAction createDistributeHorizontallyWithUniformGapsAction(IWorkbenchPage workbenchPage,
            boolean isToolbarItem) {
        DistributeAction action = new DistributeAction(workbenchPage, DistributeAction.GAPS_HORIZONTALLY,
                isToolbarItem);
        action.setId(ActionIds.DISTRIBUTE_GAPS_HORIZONTALLY);
        ImageDescriptor bundledImageDescriptor = DiagramUIPlugin.Implementation
                .getBundledImageDescriptor(DiagramImagesPath.DISTRIBUTE_WITH_UNIFORM_GAPS_HORIZONTALLY);
        action.setImageDescriptor(bundledImageDescriptor);
        action.setHoverImageDescriptor(bundledImageDescriptor);
        return action;
    }

    /**
     * Creates the Distribute action to distribute evenly centers of shapes
     * horizontally.
     * 
     * @param workbenchPage
     *            the workbench part used to obtain context
     * @param isToolbarItem
     *            the indicator of whether or not this is a toolbar action as
     *            opposed to a context-menu action.
     * @return the corresponding action
     */
    public static DistributeAction createDistributeCentersHorizontallyAction(IWorkbenchPage workbenchPage,
            boolean isToolbarItem) {
        DistributeAction action = new DistributeAction(workbenchPage, DistributeAction.CENTERS_HORIZONTALLY,
                isToolbarItem);
        action.setId(ActionIds.DISTRIBUTE_CENTERS_HORIZONTALLY);
        ImageDescriptor bundledImageDescriptor = DiagramUIPlugin.Implementation
                .getBundledImageDescriptor(DiagramImagesPath.DISTRIBUTE_CENTERS_HORIZONTALLY);
        action.setImageDescriptor(bundledImageDescriptor);
        action.setHoverImageDescriptor(bundledImageDescriptor);
        return action;
    }

    /**
     * Creates the Distribute action to distribute shapes vertically with
     * uniform gaps.
     * 
     * @param workbenchPage
     *            the workbench part used to obtain context
     * @param isToolbarItem
     *            the indicator of whether or not this is a toolbar action as
     *            opposed to a context-menu action.
     * @return the corresponding action
     */
    public static DistributeAction createDistributeVerticallyWithUniformGapsAction(IWorkbenchPage workbenchPage,
            boolean isToolbarItem) {
        DistributeAction action = new DistributeAction(workbenchPage, DistributeAction.GAPS_VERTICALLY,
                isToolbarItem);
        action.setId(ActionIds.DISTRIBUTE_GAPS_VERTICALLY);
        ImageDescriptor bundledImageDescriptor = DiagramUIPlugin.Implementation
                .getBundledImageDescriptor(DiagramImagesPath.DISTRIBUTE_WITH_UNIFORM_GAPS_VERTICALLY);
        action.setImageDescriptor(bundledImageDescriptor);
        action.setHoverImageDescriptor(bundledImageDescriptor);
        return action;
    }

    /**
     * Creates the Distribute action to distribute evenly centers of shapes
     * vertically.
     * 
     * @param workbenchPage
     *            the workbench part used to obtain context
     * @param isToolbarItem
     *            the indicator of whether or not this is a toolbar action as
     *            opposed to a context-menu action.
     * @return the corresponding action
     */
    public static DistributeAction createDistributeCentersVerticallyAction(IWorkbenchPage workbenchPage,
            boolean isToolbarItem) {
        DistributeAction action = new DistributeAction(workbenchPage, DistributeAction.CENTERS_VERTICALLY,
                isToolbarItem);
        action.setId(ActionIds.DISTRIBUTE_CENTERS_VERTICALLY);
        ImageDescriptor bundledImageDescriptor = DiagramUIPlugin.Implementation
                .getBundledImageDescriptor(DiagramImagesPath.DISTRIBUTE_CENTERS_VERTICALLY);
        action.setImageDescriptor(bundledImageDescriptor);
        action.setHoverImageDescriptor(bundledImageDescriptor);
        return action;
    }

    @Override
    protected Request createTargetRequest() {
        DistributeRequest distributionRequest = new DistributeRequest();
        distributionRequest.setDistributeType(distributeType);
        return distributionRequest;
    }

    @Override
    protected void updateTargetRequest() {
        DistributeRequest distributionRequest = (DistributeRequest) getTargetRequest();
        distributionRequest.setDistributeType(distributeType);
        distributionRequest.setEditParts(getOperationSet());
    }

    @Override
    protected Command getCommand() {
        Command cmd = null;
        List<?> operationSet = getOperationSet();
        if (!operationSet.isEmpty()) {
            EditPart targetEP = getTargetEditPartForDistributeSelection(operationSet);
            if (targetEP != null) {
                cmd = targetEP.getCommand(getTargetRequest());
            }
        }
        return cmd;
    }

    @Override
    protected List<?> createOperationSet() {
        List<?> selection = getSelectedObjects();
        if (selection.isEmpty() || !(selection.get(0) instanceof IGraphicalEditPart)) {
            selection = Collections.EMPTY_LIST;
        } else {
            // Get the the top level selected edit parts
            selection = ToolUtilities.getSelectionWithoutDependants(selection);
            // Remove the connections
            selection = Lists.newArrayList(
                    Iterables.filter(selection, Predicates.not(Predicates.instanceOf(ConnectionEditPart.class))));
            if (selection.size() < 3) {
                selection = Collections.EMPTY_LIST;
            } else {
                EditPart parent = ((EditPart) selection.get(0)).getParent();
                int sideOfFirstSelection = PositionConstants.NONE;
                if (selection.get(0) instanceof IBorderItemEditPart) {
                    // If the first selected element is a border node
                    sideOfFirstSelection = ((IBorderItemEditPart) selection.get(0)).getBorderItemLocator()
                            .getCurrentSideOfParent();
                    // Check that the side corresponds to the action axis
                    // (horizontal or vertical)
                    if (!isHorizontalAxisAuthorizedForBorderNode(sideOfFirstSelection)
                            && !isVerticalAxisAuthorizedForBorderNode(sideOfFirstSelection)) {
                        selection = Collections.EMPTY_LIST;
                    }
                }

                for (int i = 1; i < selection.size(); i++) {
                    EditPart part = (EditPart) selection.get(i);
                    if (part.getParent() != parent) {
                        // All the selected shapes must have the same parent.
                        selection = Collections.EMPTY_LIST;
                        break;
                    } else if (sideOfFirstSelection != PositionConstants.NONE
                            && !isABorderNodeOnSameAxis(part, sideOfFirstSelection)) {
                        // All the selected border nodes must have the same
                        // axis.
                        selection = Collections.EMPTY_LIST;
                        break;
                    } else if (part instanceof IGraphicalEditPart) {
                        EditPartQuery containerLayoutQuery = new EditPartQuery((IGraphicalEditPart) part);
                        if (!containerLayoutQuery.isFreeFormContainerChildrenPresentation()) {
                            // List item and elements inside compartment can not
                            // be distribute
                            selection = Collections.EMPTY_LIST;
                            break;
                        }
                    }
                }
            }
        }
        return selection;
    }

    private boolean isHorizontalAxisAuthorizedForBorderNode(int side) {
        return isOnHorizontalAxis(side) && (distributeType == DistributeAction.GAPS_HORIZONTALLY
                || distributeType == DistributeAction.CENTERS_HORIZONTALLY);
    }

    private boolean isVerticalAxisAuthorizedForBorderNode(int side) {
        return isOnVerticalAxis(side) && (distributeType == DistributeAction.GAPS_VERTICALLY
                || distributeType == DistributeAction.CENTERS_VERTICALLY);
    }

    private boolean isABorderNodeOnSameAxis(EditPart part, int expectedSide) {
        boolean result = false;
        if (part instanceof IBorderItemEditPart) {
            int currentSide = ((IBorderItemEditPart) part).getBorderItemLocator().getCurrentSideOfParent();
            if ((isOnHorizontalAxis(expectedSide) && isOnHorizontalAxis(currentSide))
                    || (isOnVerticalAxis(expectedSide) && isOnVerticalAxis(currentSide))) {
                result = true;
            }
        }
        return result;
    }

    private boolean isOnHorizontalAxis(int side) {
        return side == PositionConstants.NORTH || side == PositionConstants.SOUTH;
    }

    private boolean isOnVerticalAxis(int side) {
        return side == PositionConstants.EAST || side == PositionConstants.WEST;
    }

    @Override
    protected boolean isSelectionListener() {
        return true;
    }

    @Override
    protected boolean isOperationHistoryListener() {
        return true;
    }

    private EditPart getTargetEditPartForDistributeSelection(List<?> editparts) {
        // The Distribute request gets sent to the common parent.
        EditPart parent = ((EditPart) editparts.get(0)).getParent();
        if (parent != null) {
            // Check that the parent is the same for all selected edit parts
            // (normally it was already done via createOperationSet()).
            for (int i = 1; i < editparts.size(); i++) {
                EditPart part = (EditPart) editparts.get(i);
                // if there is no common parent, then Distribute isn't
                // supported.
                if (part.getParent() != parent) {
                    return null;
                }
            }
        }
        return parent;

    }

}