blob: adf585e8aa8e90a75e52ce5c1d83f24dd5cc7e1f [file] [log] [blame]
/*******************************************************************************
* <copyright>
*
* Copyright (c) 2005, 2017 SAP AG.
* 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:
* SAP AG - initial API, implementation and documentation
* mwenz - Bug 341224: Allow to hide the selection and marquee tools in the palette
* mwenz - Bug 363796 - Make setting of selection width of connections public
* mgorning - Bug 363186 - Allow modification of selection and hover state also for anchors
* mgorning - Bug 386913 - Support also Single-Click-Features
* mgorning - Bug 391523 - Revise getSelectionInfo...() in IToolBehaviorProvider
* fvelasco - Bug 323349 - Enable external invocation of features
* mwenz - Bug 421754 - Absolute position of active Shape nested in inactive ContainerShape is calculated incorrectly
* mwenz - Bug 428068 - Automatically unselect a tool entry in palette like 'connection creation' after execution
* mwenz - Bug 520392 - Hard coded error message when entering an empty string in direct editing
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.tb;
import java.awt.Polygon;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.graphiti.DiagramScrollingBehavior;
import org.eclipse.graphiti.IExecutionInfo;
import org.eclipse.graphiti.datatypes.ILocation;
import org.eclipse.graphiti.dt.IDiagramTypeProvider;
import org.eclipse.graphiti.features.ICreateConnectionFeature;
import org.eclipse.graphiti.features.ICreateFeature;
import org.eclipse.graphiti.features.IDirectEditingFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.ICustomContext;
import org.eclipse.graphiti.features.context.IDirectEditingContext;
import org.eclipse.graphiti.features.context.IDoubleClickContext;
import org.eclipse.graphiti.features.context.IPictogramElementContext;
import org.eclipse.graphiti.features.context.ISingleClickContext;
import org.eclipse.graphiti.features.context.impl.CustomContext;
import org.eclipse.graphiti.features.custom.ICustomFeature;
import org.eclipse.graphiti.internal.Messages;
import org.eclipse.graphiti.internal.datatypes.impl.LocationImpl;
import org.eclipse.graphiti.internal.util.T;
import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm;
import org.eclipse.graphiti.mm.algorithms.Polyline;
import org.eclipse.graphiti.mm.algorithms.styles.LineStyle;
import org.eclipse.graphiti.mm.pictograms.Anchor;
import org.eclipse.graphiti.mm.pictograms.Connection;
import org.eclipse.graphiti.mm.pictograms.ContainerShape;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.mm.pictograms.Shape;
import org.eclipse.graphiti.palette.IConnectionCreationToolEntry;
import org.eclipse.graphiti.palette.IPaletteCompartmentEntry;
import org.eclipse.graphiti.palette.impl.ConnectionCreationToolEntry;
import org.eclipse.graphiti.palette.impl.ObjectCreationToolEntry;
import org.eclipse.graphiti.palette.impl.PaletteCompartmentEntry;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.util.IColorConstant;
import org.eclipse.graphiti.util.ILocationInfo;
/**
* The Class DefaultToolBehaviorProvider.
*/
public class DefaultToolBehaviorProvider implements IToolBehaviorProvider {
/**
* The default selection width (the tolerance area a user can click to still
* select) a lathy object (e.g. {@link Polyline} or {@link Polygon} for
* {@link Shape}s and {@link Connection}s) in the diagram. Is returned in
* the default implementation of {@link #getLineSelectionWidth(Polyline)}.
*
* @since 0.9
*/
public static final int DEFAULT_LINE_SELECTION_WIDTH = 5;
protected int THRESHOLD_FOR_LONG_RUNNING_OPERATION = 20;
private static final IDecorator[] NO_RENDERING_DECORATORS = new IDecorator[0];
private static double[] ZOOM_LEVELS = { 0.01, 0.1, 0.2, 0.5, 0.75, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 10.0 };
private static final String DIAGRAM_PROPERTY_CONTRIBUTOR_SUFFIX = ".PropertyContributor"; //$NON-NLS-1$
/**
* Identifier for a generic context button, used in
* {@link DefaultToolBehaviorProvider#setGenericContextButtons(IContextButtonPadData, PictogramElement, int)}
* .
*/
protected static int CONTEXT_BUTTON_UPDATE = 1 << 1;
/**
* Identifier for a generic context button, used in
* {@link DefaultToolBehaviorProvider#setGenericContextButtons(IContextButtonPadData, PictogramElement, int)}
* .
*/
protected static int CONTEXT_BUTTON_REMOVE = 1 << 2;
/**
* Identifier for a generic context button, used in
* {@link DefaultToolBehaviorProvider#setGenericContextButtons(IContextButtonPadData, PictogramElement, int)}
* .
*/
protected static int CONTEXT_BUTTON_DELETE = 1 << 3;
/**
* The Constant NO_CONTEXT_MENU_ENTRIES.
*/
protected static final IContextMenuEntry[] NO_CONTEXT_MENU_ENTRIES = new IContextMenuEntry[0];
private IDiagramTypeProvider diagramTypeProvider;
/**
* Creates a new {@link DefaultToolBehaviorProvider}.
*
* @param diagramTypeProvider
* the diagram type provider
*/
public DefaultToolBehaviorProvider(IDiagramTypeProvider diagramTypeProvider) {
super();
setDiagramTypeProvider(diagramTypeProvider);
}
public void dispose() {
}
public Object getAdapter(Class<?> type) {
return null;
}
public PictogramElement getSelection(PictogramElement originalPe, PictogramElement[] oldSelection) {
return null;
}
/**
* @since 0.10
*/
public IConnectionSelectionInfo getSelectionInfoForConnection(Connection connection) {
IConnectionSelectionInfo si = new ConnectionSelectionInfoImpl();
si.setColor(IColorConstant.CONNECTION_SELECTION_FG);
si.setLineStyle(LineStyle.DASH);
return si;
}
public IContextButtonPadData getContextButtonPad(IPictogramElementContext context) {
final String SIGNATURE = "getContextButtonPadData(IPictogramElementContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(DefaultToolBehaviorProvider.class, SIGNATURE, new Object[] { context });
}
IContextButtonPadData ret = new DefaultContextButtonPadData();
PictogramElement pe = context.getPictogramElement();
GraphicsAlgorithm ga = getSelectionBorder(pe);
if (ga == null) {
ga = pe.getGraphicsAlgorithm();
}
// set default location
if (ga != null) {
ILocation origin = getAbsoluteLocation(ga);
ret.getPadLocation().setRectangle(origin.getX(), origin.getY(), ga.getWidth(), ga.getHeight());
}
// set default generic context buttons
setGenericContextButtons(ret, pe, CONTEXT_BUTTON_DELETE | CONTEXT_BUTTON_REMOVE | CONTEXT_BUTTON_UPDATE);
if (info) {
T.racer().exiting(DefaultToolBehaviorProvider.class, SIGNATURE, ret);
}
return ret;
}
/**
* Sets the defined generic context buttons to the given
* IContextButtonPadData. You should never change the list of generic
* context buttons directly but always use this method. This ensures, that
* only the 'generic' context buttons are set and that they have the correct
* ordering.
*
* @param data
* The IContextButtonPadData to which to set the generic context
* buttons.
* @param pe
* The pictogram element for which to get the underlying features
* of the context buttons.
* @param identifiers
* The context button identifiers (e.g. "
* {@link #CONTEXT_BUTTON_UPDATE} &
* {@link #CONTEXT_BUTTON_REMOVE}")
*/
protected void setGenericContextButtons(IContextButtonPadData data, PictogramElement pe, int identifiers) {
data.getGenericContextButtons().clear();
// update button
if ((identifiers & CONTEXT_BUTTON_UPDATE) != 0) {
IContextButtonEntry updateButton = ContextEntryHelper.createDefaultUpdateContextButton(
getFeatureProvider(), pe);
if (updateButton != null) {
data.getGenericContextButtons().add(updateButton);
}
}
// remove button
if ((identifiers & CONTEXT_BUTTON_REMOVE) != 0) {
IContextButtonEntry removeButton = ContextEntryHelper.createDefaultRemoveContextButton(
getFeatureProvider(), pe);
if (removeButton != null) {
data.getGenericContextButtons().add(removeButton);
}
}
// delete button
if ((identifiers & CONTEXT_BUTTON_DELETE) != 0) {
IContextButtonEntry deleteButton = ContextEntryHelper.createDefaultDeleteContextButton(
getFeatureProvider(), pe);
if (deleteButton != null) {
data.getGenericContextButtons().add(deleteButton);
}
}
}
/**
* Returns the location of the graphics algorithm associated to the
* pictogram element in absolute coordinates. Also see
* {@link #getAbsoluteLocation(GraphicsAlgorithm)}.
*
* @param pe
* The pictogram element, for which graphics algorithm to return
* the location.
* @return The location of the graphics algorithm associated to the
* pictogram element in absolute coordinates.
*/
protected ILocation getAbsoluteLocation(PictogramElement pe) {
return getAbsoluteLocation(pe.getGraphicsAlgorithm());
}
/**
* Returns the location of the graphics algorithm in absolute coordinates.
*
* @param ga
* The graphics algorithm for which to return the location.
* @return The location of the graphics algorithm in absolute coordinates.
*/
protected ILocation getAbsoluteLocation(GraphicsAlgorithm ga) {
PictogramElement originalPe = ga.getPictogramElement();
ILocation ret = new LocationImpl(0, 0);
while (ga != null) {
PictogramElement pe = ga.getPictogramElement();
if (pe == null || originalPe == pe || pe.isActive()) {
ret.setX(ret.getX() + ga.getX());
ret.setY(ret.getY() + ga.getY());
}
if (pe != null) {
PictogramElement parent = Graphiti.getPeService().getPictogramElementParent(pe);
if (parent != null) {
ga = parent.getGraphicsAlgorithm();
} else {
ga = null;
}
} else {
ga = ga.getParentGraphicsAlgorithm();
}
}
return ret;
}
public IContextMenuEntry[] getContextMenu(ICustomContext context) {
final String SIGNATURE = "getContextMenu(IContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(DefaultToolBehaviorProvider.class, SIGNATURE, new Object[] { context });
}
IContextMenuEntry[] ret = NO_CONTEXT_MENU_ENTRIES;
List<IContextMenuEntry> retList = new ArrayList<IContextMenuEntry>();
ICustomFeature[] customFeatures = getFeatureProvider().getCustomFeatures(context);
for (int i = 0; i < customFeatures.length; i++) {
ICustomFeature customFeature = customFeatures[i];
retList.add(new ContextMenuEntry(customFeature, context));
}
ret = retList.toArray(NO_CONTEXT_MENU_ENTRIES);
if (info) {
T.racer().exiting(DefaultToolBehaviorProvider.class, SIGNATURE, ret);
}
return ret;
}
public ILocationInfo getLocationInfo(PictogramElement pe, ILocationInfo locationInfo) {
return locationInfo;
}
public ICustomFeature getDoubleClickFeature(IDoubleClickContext context) {
return null;
}
/**
* @since 0.10
*/
public ICustomFeature getSingleClickFeature(ISingleClickContext context) {
return null;
}
/**
* @since 0.10
*/
public ICustomFeature getCommandFeature(CustomContext context, String hint) {
return null;
}
/**
* Default implementation: creates a connection and an object compartment.
* Adds all connection creation features and creation features.
*
* @return the palette entries
*/
public IPaletteCompartmentEntry[] getPalette() {
final String SIGNATURE = "getPaletteCompartments()"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(DefaultToolBehaviorProvider.class, SIGNATURE, new Object[0]);
}
List<IPaletteCompartmentEntry> compartments = new ArrayList<IPaletteCompartmentEntry>();
PaletteCompartmentEntry compartmentEntry = new PaletteCompartmentEntry(
Messages.DefaultToolBehaviorProvider_0_xfld, null);
compartments.add(compartmentEntry);
IFeatureProvider featureProvider = getFeatureProvider();
ICreateConnectionFeature[] createConnectionFeatures = featureProvider.getCreateConnectionFeatures();
if (createConnectionFeatures.length > 0) {
// ConnectionCreationToolEntry multiTool = null;
// if (createConnectionFeatures.length > 1) {
// multiTool = new ConnectionCreationToolEntry("Connection",
// "dynamic connection", null);
// compartmentEntry.addToolEntry(multiTool);
// }
for (ICreateConnectionFeature createConnectionFeature : createConnectionFeatures) {
ConnectionCreationToolEntry ccTool = new ConnectionCreationToolEntry(
createConnectionFeature.getCreateName(), createConnectionFeature.getCreateDescription(),
createConnectionFeature.getCreateImageId(), createConnectionFeature.getCreateLargeImageId());
ccTool.addCreateConnectionFeature(createConnectionFeature);
// if (multiTool != null) {
// multiTool.addCreateConnectionFeature(createConnectionFeature);
// }
compartmentEntry.addToolEntry(ccTool);
}
}
compartmentEntry = new PaletteCompartmentEntry(Messages.DefaultToolBehaviorProvider_1_xfld, null);
compartments.add(compartmentEntry);
ICreateFeature[] createFeatures = featureProvider.getCreateFeatures();
for (ICreateFeature createFeature : createFeatures) {
ObjectCreationToolEntry objectCreationToolEntry = new ObjectCreationToolEntry(
createFeature.getCreateName(), createFeature.getCreateDescription(),
createFeature.getCreateImageId(), createFeature.getCreateLargeImageId(), createFeature);
compartmentEntry.addToolEntry(objectCreationToolEntry);
}
IPaletteCompartmentEntry[] res = compartments.toArray(new IPaletteCompartmentEntry[compartments.size()]);
if (info) {
T.racer().exiting(DefaultToolBehaviorProvider.class, SIGNATURE, res);
}
return res;
}
public IDecorator[] getDecorators(PictogramElement pe) {
return NO_RENDERING_DECORATORS;
}
/**
* @since 0.10
*/
public IShapeSelectionInfo getSelectionInfoForShape(Shape shape) {
IShapeSelectionInfo si = new ShapeSelectionInfoImpl();
si.setColor(IColorConstant.SHAPE_SELECTION_FG);
si.setLineStyle(LineStyle.DASH);
return si;
}
/**
* @since 0.10
*/
public IAnchorSelectionInfo getSelectionInfoForAnchor(Anchor anchor) {
IAnchorSelectionInfo si = new AnchorSelectionInfoImpl();
si.setColor(IColorConstant.SHAPE_SELECTION_FG);
si.setLineStyle(LineStyle.DASH);
return si;
}
public double[] getZoomLevels() {
return ZOOM_LEVELS;
}
/**
* @deprecated Scroll bar based infinite canvas is a workaround for Bug
* 195527 and can be harmed by GEF modifications. It will be
* removed then
*/
@Deprecated
public DiagramScrollingBehavior getDiagramScrollingBehavior() {
return DiagramScrollingBehavior.GEF_DEFAULT;
}
private void setDiagramTypeProvider(IDiagramTypeProvider diagramTypeProvider) {
this.diagramTypeProvider = diagramTypeProvider;
}
/**
* Gets the diagram type provider.
*
* @return the diagram type provider
*/
protected IDiagramTypeProvider getDiagramTypeProvider() {
return this.diagramTypeProvider;
}
/**
* Gets the feature provider.
*
* @return the feature provider
*/
protected IFeatureProvider getFeatureProvider() {
return getDiagramTypeProvider().getFeatureProvider();
}
public void preExecute(IExecutionInfo executionInfo) {
}
public void postExecute(IExecutionInfo executionInfo) {
}
/**
* Returns the tooltip to be attached to the graphical representation of the
* given graphics algorithm.
*
* @param graphicsAlgorithm
* the graphics algorithm
* @return the tooltip
* @since 0.10
*/
public Object getToolTip(GraphicsAlgorithm ga) {
return null;
}
public GraphicsAlgorithm getSelectionBorder(PictogramElement pe) {
return null;
}
public GraphicsAlgorithm[] getClickArea(PictogramElement pe) {
GraphicsAlgorithm graphicsAlgorithm = pe.getGraphicsAlgorithm();
if (graphicsAlgorithm != null) {
return new GraphicsAlgorithm[] { graphicsAlgorithm };
}
return new GraphicsAlgorithm[0];
}
public GraphicsAlgorithm getChopboxAnchorArea(PictogramElement pe) {
return pe.getGraphicsAlgorithm();
}
public String getTitleToolTip() {
return null;
}
public boolean isShowGuides() {
return true;
}
public GraphicsAlgorithm getContentArea(ContainerShape cs) {
return null;
}
public String getContributorId() {
String diagramTypeId = getDiagramTypeProvider().getDiagram().getDiagramTypeId();
return diagramTypeId + DIAGRAM_PROPERTY_CONTRIBUTOR_SUFFIX;
}
public boolean isDefaultBendPointRenderingActive() {
return true;
}
public boolean isMultiSelectionEnabled() {
return true;
}
public boolean isConnectionSelectionEnabled() {
return true;
}
/**
* Default Implementation. Customers requested the possibility to plug in
* equality decision based on object identity.
*/
public boolean equalsBusinessObjects(Object o1, Object o2) {
if (o1 instanceof EObject && o2 instanceof EObject) {
return EcoreUtil.equals((EObject) o1, (EObject) o2);
}
// Both BOs have to be EMF objects. Otherwise the IndependenceSolver
// does the job.
return false;
}
/**
* {@see IToolBehaviorProvider#isShowFlyoutPalette()}. This default
* implementation returns <code>true</code> and tells the framework to show
* the palette.
*
* @return <code>true</code>
*
* @since 0.9
*/
public boolean isShowFlyoutPalette() {
return true;
}
/**
* {@see IToolBehaviorProvider#isShowSelectionTool()}. This default
* implementation returns <code>true</code> and tells the framework to show
* the selection tool entry in the palette.
*
* @return <code>true</code>
*
* @since 0.9
*/
public boolean isShowSelectionTool() {
return true;
}
/**
* {@see IToolBehaviorProvider#isShowMarqueeTool()}. This default
* implementation returns <code>true</code> and tells the framework to show
* the marquee tool entry in the palette.
*
* @return <code>true</code>
*
* @since 0.9
*/
public boolean isShowMarqueeTool() {
return true;
}
/**
* Is asked to return the selection width (the tolerance area a user can
* click to still select) a lathy object (e.g. {@link Polyline} or
* {@link Polygon} for {@link Shape}s and {@link Connection}s) in the
* diagram. The method is called when a new object is drawn for the first
* time onto a diagram (on creation of the object or on opening the
* diagram).<br>
* The default implementation returns {@link #DEFAULT_LINE_SELECTION_WIDTH}
* with the value {@value #DEFAULT_LINE_SELECTION_WIDTH} for all shapes.
*
* @param polyline
* the {@link Polyline} object to get the selection width for
* @return an int representing the allowed tolerance for clicking in pixels
*
* @since 0.9
*/
public int getLineSelectionWidth(Polyline polyline) {
return DEFAULT_LINE_SELECTION_WIDTH;
}
/**
* Defines if the tool created for the given
* {@link IConnectionCreationToolEntry} stays active after a connection has
* been created or not. The default behavior in the GEF framework is
* <code>true</code>, which is also the return value of this default
* implementation.
*
* @return <code>true</code> in case the tool should stay active after
* execution, <code>false</code> otherwise.
*
* @since 0.11
*/
@Override
public boolean isStayActiveAfterExecution(IConnectionCreationToolEntry connectionCreationToolEntry) {
return true;
}
/**
* Returns the title of the popup that appears when a user provides an
* invalid input for direct editing. This default implementation returns
* 'Can not edit value'.
*
* @param feature
* The {@link IDirectEditingFeature} that was invoked
* @param context
* The {@link IDirectEditingContext} that was used.
* @return A {@link String} representing the title of the popup to present
* to the user.
* @since 0.15
*/
@Override
public String getDirectEditingInvalidNotificationTitle(IDirectEditingFeature feature,
IDirectEditingContext context) {
return Messages.DefaultDirectEditPolicy_0_xmsg;
}
}