blob: a06fbfcf3102f6d3e493288ab6e7385b7479ae05 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2020 IBM Corporation and others.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.sirius.diagram.ui.tools.internal.actions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.Request;
import org.eclipse.gmf.runtime.diagram.ui.actions.DiagramAction;
import org.eclipse.gmf.runtime.diagram.ui.actions.internal.l10n.DiagramUIActionsMessages;
import org.eclipse.gmf.runtime.diagram.ui.actions.internal.l10n.DiagramUIActionsPluginImages;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.GroupEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IPrimaryEditPart;
import org.eclipse.gmf.runtime.diagram.ui.internal.editparts.ISurfaceEditPart;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.sirius.diagram.ui.tools.api.ui.actions.ActionIds;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.actions.ActionFactory;
/**
* Sirius specific select all action: Only to always launch refresh in UI thread. This new class has been created
* waiting the resolution in GMF of bugzilla 567128.<BR>
* Only the method refresh is not from the original class
* {@link org.eclipse.gmf.runtime.diagram.ui.actions.internal.SelectAllAction}. The field siriusOperationSet has been
* also added instead of private _operationSet. So all methods using this field have also been duplicated (dispose,
* getSiriusOperationSet, and internalRefresh).
*
* @author lredor
*
*/
// CHECKSTYLE:OFF
public class SiriusSelectAllAction extends DiagramAction {
/**
* the cached operation set. This field replaces {@link DiagramAction#_operationSet} as it is private in super class
* (used in dispose(), getOperationSet() and refresh());.
*/
private List siriusOperationSet = Collections.EMPTY_LIST;
/** whether to select shapes */
private boolean selectShapes;
/** whether to select connections */
private boolean selectConnections;
/**
* @param partService
* @param selectShapes
* @param selectConnections
*/
private SiriusSelectAllAction(IWorkbenchPage partService, boolean selectShapes, boolean selectConnections) {
super(partService);
this.selectShapes = selectShapes;
this.selectConnections = selectConnections;
}
protected Request createTargetRequest() {
return null;
}
/**
* The operation set is the shapes, connections or both on the diagram edit part
*/
protected List createOperationSet() {
List selection = getSelectedObjects();
if (selection.isEmpty() || !(selection.get(0) instanceof IGraphicalEditPart))
return Collections.EMPTY_LIST;
List selectables = new ArrayList();
EditPart primaryEP = (EditPart) selection.get(selection.size() - 1);
List nodeEditParts = new ArrayList();
nodeEditParts.addAll(getSelectableNodes(primaryEP));
if (selectShapes)
selectables.addAll(nodeEditParts);
if (selectConnections)
selectables.addAll(addSelectableConnections(nodeEditParts));
return filterEditPartsMatching(selectables, getSelectionConditional());
}
/**
* Determines the candidate list of node editparts for selection
*
* @param editpart
* @return
*/
protected List getSelectableNodes(EditPart editpart) {
if (editpart == null) {
return Collections.EMPTY_LIST;
}
List retval = new ArrayList();
getSelectableNodesInside(editpart, true, retval);
return retval;
}
/**
* Determines the candidate list of node editparts for selection
*
* @param editpart
* @param topLevel
* <code>boolean</code> is this the initial entry point into the recursive method.
* @param retval
* <code>List</code> to modify
*/
private void getSelectableNodesInside(EditPart editpart, boolean topLevel, List retval) {
if (editpart instanceof ISurfaceEditPart) {
getSelectableChildrenNodes(editpart, retval);
} else if (editpart instanceof IPrimaryEditPart) {
if (topLevel) {
if (editpart instanceof ConnectionEditPart) {
ConnectionEditPart connection = (ConnectionEditPart) editpart;
EditPart source = connection.getSource();
EditPart target = connection.getTarget();
if (source != null && target != null) {
getSelectableNodesInside(source, true, retval);
if (target.getParent() != source.getParent())
getSelectableNodesInside(target, true, retval);
}
} else
getSelectableNodesInside(editpart.getParent(), true, retval);
} else {
if (editpart.isSelectable())
retval.add(editpart);
// Do not dig into groups -- just select the group, but not the
// shapes inside.
if (!(editpart instanceof GroupEditPart)) {
getSelectableChildrenNodes(editpart, retval);
}
}
}
}
private void getSelectableChildrenNodes(EditPart editpart, List retval) {
Iterator iter = editpart.getChildren().iterator();
while (iter.hasNext()) {
EditPart child = (EditPart) iter.next();
getSelectableNodesInside(child, false, retval);
}
}
/**
* This method searches an edit part for a child that is a border item edit part
*
* @param parent
* part needed to search
* @param set
* to be modified of border item edit parts that are direct children of the parent
*/
private void getBorderItemEditParts(EditPart parent, Set retval) {
Iterator iter = parent.getChildren().iterator();
while (iter.hasNext()) {
EditPart child = (EditPart) iter.next();
if (child instanceof IBorderItemEditPart) {
retval.add(child);
retval.addAll(child.getChildren());
}
getBorderItemEditParts(child, retval);
}
}
/**
* Determines the candidate list of connection edit for selection A connection is included if atleast the source or
* the target is included in the given list
*
* @param editparts
*/
protected List addSelectableConnections(List editparts) {
List selectableConnections = new ArrayList();
DiagramEditPart diagramEditPart = getDiagramEditPart();
Set connnectableEditParts = new HashSet(editparts);
ListIterator li = editparts.listIterator();
while (li.hasNext()) {
EditPart ep = (EditPart) li.next();
getBorderItemEditParts(ep, connnectableEditParts);
if (ep instanceof GroupEditPart) {
connnectableEditParts.addAll(((GroupEditPart) ep).getShapeChildren());
}
}
if (diagramEditPart != null) {
Iterator connections = diagramEditPart.getConnections().iterator();
while (connections.hasNext()) {
ConnectionEditPart connection = (ConnectionEditPart) connections.next();
if (canSelectConnection(connection, connnectableEditParts))
selectableConnections.add(connection);
}
}
return selectableConnections;
}
/**
* Determines whether the given connection can be selected. First checks whether the source or target of the
* connection is in the given connetableEditPart list. If it isn't it checks recursively whether the source or
* target of the connection is another connection and if that connection's source or target is in the given
* connectableEditPart list. This is in response to Bugzilla #162083.
*
* @param connection
* connection to check
* @param connectableEditParts
*/
private boolean canSelectConnection(ConnectionEditPart connection, Set connectableEditParts) {
EditPart connectionSource = connection.getSource();
EditPart connectionTarget = connection.getTarget();
boolean sourceHasSelectable = false;
boolean targetHasSelectable = false;
if (connectableEditParts.contains(connectionSource) || connectableEditParts.contains(connectionTarget))
return true;
if (connectionSource instanceof ConnectionEditPart)
sourceHasSelectable = canSelectConnection((ConnectionEditPart) connectionSource, connectableEditParts);
if (!sourceHasSelectable && connectionTarget instanceof ConnectionEditPart)
targetHasSelectable = canSelectConnection((ConnectionEditPart) connectionTarget, connectableEditParts);
return sourceHasSelectable || targetHasSelectable;
}
/**
* @return The Selection Conditional which tests if the editpart is selectable
*/
protected EditPartViewer.Conditional getSelectionConditional() {
return new EditPartViewer.Conditional() {
public boolean evaluate(EditPart editpart) {
return editpart.isSelectable();
}
};
}
/**
* Returns true if the operation set is not empty and only if the diagram is selected.
*/
protected boolean calculateEnabled() {
return !getSiriusOperationSet().isEmpty();
}
protected void doRun(IProgressMonitor progressMonitor) {
getDiagramGraphicalViewer().setSelection(new StructuredSelection(getSiriusOperationSet()));
}
protected boolean isSelectionListener() {
return true;
}
protected boolean isOperationHistoryListener() {
// certain undo/redo actions can cause selection to change. see bugzilla#156261
return true;
}
/**
* Create the SelectAll action
*
* @return The SelectAll action
*/
public static SiriusSelectAllAction createSelectAllAction(IWorkbenchPage partService) {
SiriusSelectAllAction action = new SiriusSelectAllAction(partService, true, true);
action.setId(ActionFactory.SELECT_ALL.getId());
action.setText(DiagramUIActionsMessages.SelectAllAction_SelectAll);
action.setToolTipText(DiagramUIActionsMessages.SelectAllAction_SelectAll);
action.setImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTALL);
action.setDisabledImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTALL_DISABLED);
return action;
}
/**
* Create the SelectAll toolbar action
*
* @return The SelectAll toobar action
*/
public static SiriusSelectAllAction createToolbarSelectAllAction(IWorkbenchPage partService) {
SiriusSelectAllAction action = new SiriusSelectAllAction(partService, true, true);
action.setId(ActionIds.ACTION_SIRIUS_TOOLBAR_SELECT_ALL);
action.setText(DiagramUIActionsMessages.SelectAllAction_toolbar_SelectAll);
action.setToolTipText(DiagramUIActionsMessages.SelectAllAction_toolbar_SelectAll);
action.setImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTALL);
action.setDisabledImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTALL_DISABLED);
return action;
}
/**
* Create the SelectAllShapes action
*
* @return The SelectAllShapes action
*/
public static SiriusSelectAllAction createSelectAllShapesAction(IWorkbenchPage partService) {
SiriusSelectAllAction action = new SiriusSelectAllAction(partService, true, false);
action.setId(ActionIds.ACTION_SIRIUS_SELECT_ALL_SHAPES);
action.setText(DiagramUIActionsMessages.SelectAllAction_SelectShapes);
action.setToolTipText(DiagramUIActionsMessages.SelectAllAction_SelectShapes);
action.setImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTSHAPES);
action.setDisabledImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTSHAPES_DISABLED);
return action;
}
/**
* Create the SelectAllShapes toolbar action
*
* @return The SelectAllShapes toolbar action
*/
public static SiriusSelectAllAction createToolbarSelectAllShapesAction(IWorkbenchPage partService) {
SiriusSelectAllAction action = new SiriusSelectAllAction(partService, true, false);
action.setId(ActionIds.ACTION_SIRIUS_TOOLBAR_SELECT_ALL_SHAPES);
action.setText(DiagramUIActionsMessages.SelectAllAction_toolbar_SelectShapes);
action.setToolTipText(DiagramUIActionsMessages.SelectAllAction_toolbar_SelectShapes);
action.setImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTSHAPES);
action.setDisabledImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTSHAPES_DISABLED);
return action;
}
/**
* Create the SelectAllConnections action
*
* @return The SelectAllConnections action
*/
public static SiriusSelectAllAction createSelectAllConnectionsAction(IWorkbenchPage partService) {
SiriusSelectAllAction action = new SiriusSelectAllAction(partService, false, true);
action.setId(ActionIds.ACTION_SIRIUS_SELECT_ALL_CONNECTIONS);
action.setText(DiagramUIActionsMessages.SelectAllAction_SelectConnections);
action.setToolTipText(DiagramUIActionsMessages.SelectAllAction_SelectConnections);
action.setImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTCONNECTIONS);
action.setDisabledImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTCONNECTIONS_DISABLED);
return action;
}
/**
* Create the SelectAllConnections toolbar action
*
* @return The SelectAllConnections toolbar action
*/
public static SiriusSelectAllAction createToolbarSelectAllConnectionsAction(IWorkbenchPage partService) {
SiriusSelectAllAction action = new SiriusSelectAllAction(partService, false, true);
action.setId(ActionIds.ACTION_SIRIUS_TOOLBAR_SELECT_ALL_CONNECTIONS);
action.setText(DiagramUIActionsMessages.SelectAllAction_toolbar_SelectConnections);
action.setToolTipText(DiagramUIActionsMessages.SelectAllAction_toolbar_SelectConnections);
action.setImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTCONNECTIONS);
action.setDisabledImageDescriptor(DiagramUIActionsPluginImages.DESC_SELECTCONNECTIONS_DISABLED);
return action;
}
@Override
public void dispose() {
super.dispose();
siriusOperationSet = null;
}
/**
* A copy of {@link DiagramAction#getOperationSet()} to manipulate siriusOperationSet instead of _operationSet.
*
* Return the list of editparts considered the operation set after caching them
*
* @return A list of editparts conidered the operation set
*/
protected final List getSiriusOperationSet() {
if (siriusOperationSet == null) {
siriusOperationSet = createOperationSet();
if (siriusOperationSet == null)
siriusOperationSet = Collections.EMPTY_LIST;
}
return siriusOperationSet;
}
@Override
public void refresh() {
if (Display.getCurrent() == null) {
/*
* We are not in a UI thread, so we call the refresh later to avoid potential
* ConcurrentModificationException or worse.
*/
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
internalRefresh();
}
});
} else {
/* Here we are in UI Thread */
internalRefresh();
}
}
/**
* A copy of original {@link DiagramAction#refresh()} but using siriusOperationSet instead of _operationSet.
*/
public void internalRefresh() {
siriusOperationSet = null; // invalidate the cached set
updateTargetRequest();
setEnabled(calculateEnabled());
}
}
// CHECKSTYLE:ON