blob: d9b2c1b306be6815464d37d9a29fd7eb8b35eeb1 [file] [log] [blame]
/*******************************************************************************
* <copyright>
*
* Copyright (c) 2005, 2014 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
* Volker Wegert - Bug 336828: patterns should support delete,
* remove, direct editing and conditional palette
* creation entry
* mwenz - Bug 325084 - Provide documentation for Patterns
* cbrand - Bug 376585 - Clean-up deprecations in Graphiti
* cbrand - Bug 385190 - Introduce constructor without parameters for patterns
* mwenz - Bug 390331 - preDelete and postDelete not called for Patterns
* mwenz - Bug 453553 - Provide an abort possibility for delete and remove features in case 'pre' methods fail
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.graphiti.features.DefaultResizeConfiguration;
import org.eclipse.graphiti.features.IDeleteFeature;
import org.eclipse.graphiti.features.IDirectEditingInfo;
import org.eclipse.graphiti.features.IReason;
import org.eclipse.graphiti.features.IRemoveFeature;
import org.eclipse.graphiti.features.IResizeConfiguration;
import org.eclipse.graphiti.features.context.IAreaContext;
import org.eclipse.graphiti.features.context.ICreateContext;
import org.eclipse.graphiti.features.context.IDeleteContext;
import org.eclipse.graphiti.features.context.IDirectEditingContext;
import org.eclipse.graphiti.features.context.ILayoutContext;
import org.eclipse.graphiti.features.context.IMoveShapeContext;
import org.eclipse.graphiti.features.context.IRemoveContext;
import org.eclipse.graphiti.features.context.IResizeShapeContext;
import org.eclipse.graphiti.features.context.IUpdateContext;
import org.eclipse.graphiti.features.context.impl.AddContext;
import org.eclipse.graphiti.features.context.impl.LayoutContext;
import org.eclipse.graphiti.features.context.impl.UpdateContext;
import org.eclipse.graphiti.features.impl.AbstractCreateFeature;
import org.eclipse.graphiti.features.impl.AbstractDirectEditingFeature;
import org.eclipse.graphiti.features.impl.AbstractLayoutFeature;
import org.eclipse.graphiti.features.impl.AbstractUpdateFeature;
import org.eclipse.graphiti.features.impl.DefaultMoveShapeFeature;
import org.eclipse.graphiti.features.impl.DefaultRemoveFeature;
import org.eclipse.graphiti.features.impl.DefaultResizeShapeFeature;
import org.eclipse.graphiti.features.impl.Reason;
import org.eclipse.graphiti.func.IDelete;
import org.eclipse.graphiti.func.IDirectEditing;
import org.eclipse.graphiti.func.IProposalSupport;
import org.eclipse.graphiti.func.IRemove;
import org.eclipse.graphiti.mm.algorithms.styles.Point;
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.FreeFormConnection;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.mm.pictograms.Shape;
import org.eclipse.graphiti.pattern.config.IPatternConfiguration;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.ui.features.DefaultDeleteFeature;
/**
* This is the base class AbstractConnectionPattern that clients writing a
* pattern for a shape domain object should subclass.
*/
public abstract class AbstractPattern extends AbstractBasePattern implements IPattern {
/**
* An empty string array used in direct editing.
*/
protected static final String[] EMPTY_STRING_ARRAY = new String[0];
private IPatternConfiguration patternConfiguration;
/**
* To avoid code duplication, this base class uses a wrapped default
* implementation of an {@link IDeleteFeature} to provide the default
* deletion behaviour. Subclasses may decide to either override
* {@link #createDeleteFeature(IDeleteContext)} to provide another
* {@link IDeleteFeature} implementation or override and extend the
* individual {@link IDelete} methods or return a {@link IDeleteFeature} by
* overriding the method
* {@link DefaultFeatureProviderWithPatterns#getDeleteFeature(IDeleteContext)}
* .
*/
private IDeleteFeature wrappedDeleteFeature;
/**
* To avoid code duplication, this base class uses a wrapped default
* implementation of an {@link IRemoveFeature} to provide the default
* removal behavior. Subclasses may decide to either override
* {@link #createRemoveFeature(IRemoveContext)} to provide another
* {@link IRemoveFeature} implementation or override and extend the
* individual {@link IRemove} methods or return a {@link IRemoveFeature} by
* overriding the method
* {@link DefaultFeatureProviderWithPatterns#getRemoveFeature(IRemoveContext)}
* .
*/
private IRemoveFeature wrappedRemoveFeature;
/**
* Creates a new {@link AbstractPattern} holding the given
* {@link IPatternConfiguration}.
*
* @param patternConfiguration
* The pattern configuration to use within this pattern instance
* of <code>null</code> in case no configuration is needed.
*/
public AbstractPattern(IPatternConfiguration patternConfiguration) {
this();
setPatternConfiguration(patternConfiguration);
}
/**
* Creates a new {@link AbstractPattern}. This is a convenience method for
* patterns working without any configuration.
*
* @since 0.10
*/
public AbstractPattern() {
super();
}
/**
* Is queried by the Graphiti framework to check if the pattern should
* create a new domain object entry in the editor palette.
*
* @return <code>true</code> in case a palette entry shall be created,
* <code>false</code> otherwise.
*/
public boolean isPaletteApplicable() {
return true;
}
/**
* Clients must override this method to indicate that the pattern can be
* used to create domain objects as defined in the given
* {@link ICreateContext}. Corresponds to the method
* {@link AbstractCreateFeature#canCreate(ICreateContext)} . The default
* implementation simply returns <code>false</code>.
*
* @param context
* The context holding information on the domain object to be
* created.
* @return <code>true</code> in case this pattern can create such a domain
* object, <code>false</code> otherwise.
*/
public boolean canCreate(ICreateContext context) {
return false;
}
/**
* Clients may override this method to indicate that the pattern can be used
* to layout a shape for a domain objects as defined in the given
* {@link ILayoutContext}. Corresponds to the method
* {@link AbstractLayoutFeature#canLayout(ILayoutContext)}. The default
* implementation checks if the {@link PictogramElement} in the given
* context {@link #isPatternControlled(PictogramElement)}.
*
* @param context
* The context holding information on the domain object to be
* layouted.
* @return <code>true</code> in case this pattern can layout a shape for
* such a domain object, <code>false</code> otherwise.
*/
public boolean canLayout(ILayoutContext context) {
PictogramElement pictogramElement = context.getPictogramElement();
return isPatternControlled(pictogramElement);
}
/**
* Clients may override this method to indicate that the pattern can be used
* to move the shape of a domain objects as defined in the given
* {@link IMoveShapeContext}. Corresponds to the method
* {@link DefaultMoveShapeFeature#canMoveShape(IMoveShapeContext)}. The
* default implementation checks if the {@link PictogramElement} in the
* given context {@link #isPatternControlled(PictogramElement)} and the
* source and target containers of the shape are the same.
*
* @param context
* The context holding information on the domain object to be
* moved.
* @return <code>true</code> in case this pattern can move a shape for such
* a domain object, <code>false</code> otherwise.
*/
public boolean canMoveShape(IMoveShapeContext context) {
return context.getSourceContainer() != null
&& context.getSourceContainer().equals(context.getTargetContainer())
&& isPatternRoot(context.getPictogramElement());
}
/**
* Clients may override this method to indicate that the pattern can be used
* to resize the shape of a domain objects as defined in the given
* {@link IResizeShapeContext}. Corresponds to the method
* {@link DefaultResizeShapeFeature#canResizeShape(IResizeShapeContext)}.
* The default implementation checks if the {@link PictogramElement} in the
* given context fulfills {@link #isPatternRoot(PictogramElement)}.
*
* @param context
* The context holding information on the domain object to be
* resized.
* @return <code>true</code> in case this pattern can resize a shape for
* such a domain object, <code>false</code> otherwise.
*/
public boolean canResizeShape(IResizeShapeContext context) {
return isPatternRoot(context.getPictogramElement());
}
/**
* Clients may override this method to indicate that the pattern can be used
* to update the shape of a domain objects as defined in the given
* {@link IUpdateContext}. Corresponds to the method
* {@link AbstractUpdateFeature#canUpdate(IUpdateContext)}. The default
* implementation checks if the {@link PictogramElement} in the given
* context {@link #isPatternControlled(PictogramElement)}.
*
* @param context
* The context holding information on the domain object to be
* updated.
* @return <code>true</code> in case this pattern can update a shape for
* such a domain object, <code>false</code> otherwise.
*/
public boolean canUpdate(IUpdateContext context) {
PictogramElement pictogramElement = context.getPictogramElement();
return isPatternControlled(pictogramElement);
}
/**
* Clients must override this method to implement the functionality to
* create a new domain object as defined in the given {@link ICreateContext}
* . Corresponds to the method
* {@link AbstractCreateFeature#create(ICreateContext)}. The default
* implementation simply does nothing and returns an empty object array.
*
* @param context
* The context holding information on the domain object to be
* created.
* @return An array of the newly create domain objects.
*/
public Object[] create(ICreateContext context) {
return EMPTY;
}
/**
* Client should override to return a string description of the type of
* domain object that is created with this pattern. The Graphiti framework
* uses this information to fill a tooltip for the creation tool entry in
* the palette. The default implementation simply returns <code>null</code>
* which indicates that no tooltip shall be displayed.
*
* @return A {@link String} holding the tooltip
*/
public String getCreateDescription() {
return null;
}
/**
* Client should override to return a string id of the the image icon for
* the domain object that is created with this pattern. The Graphiti
* framework uses this information to add an icon to the creation tool entry
* in the palette. The default implementation simply returns
* <code>null</code> which indicates that no icon shall be displayed.
*
* @return A {@link String} holding the id of the icon as defined in the
* AbstractImageProvider.
*/
public String getCreateImageId() {
return null;
}
/**
* Client should override to return a string id of the the large image icon
* for the domain object that is created with this pattern. The Graphiti
* framework uses this information to add a large icon to the creation tool
* entry in the palette. The default implementation simply returns
* <code>null</code> which indicates that no icon shall be displayed.
*
* @return A {@link String} holding the id of the large icon as defined in
* the AbstractImageProvider.
*/
public String getCreateLargeImageId() {
return getCreateImageId();
}
/**
* Client should override to return the name of the domain object that is
* created with this pattern. The Graphiti framework uses this information
* to fill the text for the creation tool entry in the palette. The default
* implementation simply returns <code>null</code> which results in an empty
* entry in the palette.
*
* @return A {@link String} holding the name of the domain object.
*/
public String getCreateName() {
return null;
}
/**
* Clients must override this method to indicate that the pattern uses the
* given domain object as its main domain object.
*
* @param mainBusinessObject
* The object to check if it is the main domain object of the
* pattern.
* @return <code>true</code> in case the pattern has the given domain object
* as its main domain object, <code>false</code> otherwise.
*/
abstract public boolean isMainBusinessObjectApplicable(Object mainBusinessObject);
/**
* Clients can override this method to implement the functionality to layout
* a shape for the given domain object as defined in the given
* {@link ILayoutContext} . Corresponds to the method
* {@link AbstractLayoutFeature#layout(ILayoutContext)}. The default
* implementation simply does nothing and returns <code>false</code> as
* indication of this.
*
* @param context
* The context holding information on the domain object to be
* layouted.
* @return Should return <code>true</code> in case a layout happened and
* <code>false</code> in case none happened. Is used by the Graphiti
* framework for performance optimization.
*/
public boolean layout(ILayoutContext context) {
return false;
}
/**
* Clients can override this method to implement the functionality to move a
* shape for the given domain object as defined in the given
* {@link IMoveShapeContext} . Corresponds to the method
* {@link DefaultMoveShapeFeature#moveShape(IMoveShapeContext)}.
*
* @param context
* The context holding information on the domain object to be
* moved.
*/
public void moveShape(IMoveShapeContext context) {
preMoveShape(context);
moveAllBendpoints(context);
internalMove(context);
postMoveShape(context);
}
/**
* Hook clients can override to add additional steps after the move of the
* shape happened.
*
* @param context
* The context holding information on the domain object that was
* moved.
*/
protected void postMoveShape(IMoveShapeContext context) {
}
/**
* Hook clients can override to add additional steps before the move of the
* shape happens.
*
* @param context
* The context holding information on the domain object to be
* moved.
*/
protected void preMoveShape(IMoveShapeContext context) {
}
/**
* Default implementation of the move functionality. Moves shapes to new
* coordinates and adapts parents in case this is needed.
*
* @param context
* The context holding information on the domain object to be
* moved.
*/
protected void internalMove(IMoveShapeContext context) {
Shape shapeToMove = context.getShape();
ContainerShape oldContainerShape = context.getSourceContainer();
ContainerShape newContainerShape = context.getTargetContainer();
int x = context.getX();
int y = context.getY();
if (oldContainerShape != newContainerShape) {
// the following is a workaround due to an MMR bug
if (oldContainerShape != null) {
Collection<Shape> children = oldContainerShape.getChildren();
if (children != null) {
children.remove(shapeToMove);
}
}
shapeToMove.setContainer(newContainerShape);
if (shapeToMove.getGraphicsAlgorithm() != null) {
Graphiti.getGaService().setLocation(shapeToMove.getGraphicsAlgorithm(), x, y,
avoidNegativeCoordinates());
}
} else { // move within the same container
if (shapeToMove.getGraphicsAlgorithm() != null) {
Graphiti.getGaService().setLocation(shapeToMove.getGraphicsAlgorithm(), x, y,
avoidNegativeCoordinates());
}
}
}
/**
* Default implementation of the move functionality to move all bendpoints
* within a container shape.
*
* @param context
* The context holding information on the domain object to be
* moved.
*/
protected void moveAllBendpoints(IMoveShapeContext context) {
if (!(context.getShape() instanceof ContainerShape)) {
return;
}
ContainerShape shapeToMove = (ContainerShape) context.getShape();
int x = context.getX();
int y = context.getY();
int deltaX = x - shapeToMove.getGraphicsAlgorithm().getX();
int deltaY = y - shapeToMove.getGraphicsAlgorithm().getY();
if (deltaX != 0 || deltaY != 0) {
List<Anchor> anchorsFrom = getAnchors(shapeToMove);
List<Anchor> anchorsTo = new ArrayList<Anchor>(anchorsFrom);
for (Anchor anchorFrom : anchorsFrom) {
Collection<Connection> outgoingConnections = anchorFrom.getOutgoingConnections();
for (Connection connection : outgoingConnections) {
for (Anchor anchorTo : anchorsTo) {
Collection<Connection> incomingConnections = anchorTo.getIncomingConnections();
if (incomingConnections.contains(connection)) {
if (connection instanceof FreeFormConnection) {
FreeFormConnection ffc = (FreeFormConnection) connection;
List<Point> points = ffc.getBendpoints();
for (int i = 0; i < points.size(); i++) {
Point point = points.get(i);
int oldX = point.getX();
int oldY = point.getY();
points.set(i, Graphiti.getGaService().createPoint(oldX + deltaX, oldY + deltaY));
}
}
}
}
}
}
}
}
private List<Anchor> getAnchors(ContainerShape containerShape) {
List<Anchor> ret = new ArrayList<Anchor>();
ret.addAll(containerShape.getAnchors());
List<Shape> children = containerShape.getChildren();
for (Shape shape : children) {
if (shape instanceof ContainerShape) {
ret.addAll(getAnchors((ContainerShape) shape));
} else {
ret.addAll(shape.getAnchors());
}
}
return ret;
}
/**
* Clients can override this method to implement the functionality to resize
* a shape for the given domain object as defined in the given
* {@link IResizeShapeContext} . Corresponds to the method
* {@link DefaultResizeShapeFeature#resizeShape(IResizeShapeContext)}.
*
* @param context
* The context holding information on the domain object to be
* resized.
*/
public void resizeShape(IResizeShapeContext context) {
Shape shape = context.getShape();
int x = context.getX();
int y = context.getY();
int width = context.getWidth();
int height = context.getHeight();
if (shape.getGraphicsAlgorithm() != null) {
Graphiti.getGaService().setLocationAndSize(shape.getGraphicsAlgorithm(), x, y, width, height);
}
layoutPictogramElement(shape);
}
/**
* Clients can override this method to implement the functionality to update
* a shape for the given domain object as defined in the given
* {@link IUpdateContext}. Corresponds to the method
* {@link AbstractUpdateFeature#update(IUpdateContext)}.
*
* @param context
* The context holding information on the domain object to be
* updated.
*/
public boolean update(IUpdateContext context) {
return false;
}
/**
* Clients can override this method to indicate if an update of a shape for
* the given domain object as defined in the given {@link IUpdateContext}
* needs to be triggered. Corresponds to the method
* {@link AbstractUpdateFeature#updateNeeded(IUpdateContext)}.
*
* @param context
* The context holding information on the domain object to be
* updated.
*/
public IReason updateNeeded(IUpdateContext context) {
return Reason.createFalseReason();
}
/**
* Adds the graphical representation of the given new {@link Object} with
* the information in the given {@link IAreaContext}.
*
* @param context
* The area context defining where the new object should placed
* @param newObject
* The new object instance itself
*/
protected void addGraphicalRepresentation(IAreaContext context, Object newObject) {
getFeatureProvider().addIfPossible(new AddContext(context, newObject));
}
/**
* Clients can override to indicate that moving to negative coordinates
* should be possible. The default implementation prohibits this by
* returning false.
*
* @return <code>true</code> in case moving a shape to negative coordinates
* should be possible, <code>false</code> otherwise.
*/
protected boolean avoidNegativeCoordinates() {
return false;
}
/**
* This method must be implemented by clients to indicate that the given
* {@link PictogramElement} is controlled by this pattern.
*
* @param pictogramElement
* The pictogram element to check
* @return <code>true</code> in case the pictogram element is controlled by
* this pattern, <code>false</code> otherwise.
*/
abstract protected boolean isPatternControlled(PictogramElement pictogramElement);
/**
* This method must be implemented by clients to indicate that the given
* {@link PictogramElement} is the root shape of this pattern.
*
* @param pictogramElement
* The pictogram element to check
* @return <code>true</code> in case the pictogram element is the root shape
* of this pattern, <code>false</code> otherwise.
*/
abstract protected boolean isPatternRoot(PictogramElement pictogramElement);
/**
* Helper method that triggers a layout of the given
* {@link PictogramElement}. The default implementation queries the feature
* provider and tries to find a functionality either in the pattern of an
* additional {@link AbstractLayoutFeature} that can handle the request and
* triggers the operation.
*
* @param pe
* The pictogram element to layout
*/
protected void layoutPictogramElement(PictogramElement pe) {
LayoutContext context = new LayoutContext(pe);
getFeatureProvider().layoutIfPossible(context);
}
/**
* Helper method that triggers an update of the given
* {@link PictogramElement}. The default implementation queries the feature
* provider and tries to find a functionality either in the pattern of an
* additional {@link AbstractUpdateFeature} that can handle the request and
* triggers the operation.
*
* @param pe
* The pictogram element to update
*/
protected void updatePictogramElement(PictogramElement pe) {
UpdateContext context = new UpdateContext(pe);
getFeatureProvider().updateIfPossible(context);
layoutPictogramElement(pe);
}
/**
* Sets the {@link IPatternConfiguration} instance to be used with this
* pattern.
*
* @param patternConfiguration
* The new patternConfiguration
*/
protected void setPatternConfiguration(IPatternConfiguration patternConfiguration) {
this.patternConfiguration = patternConfiguration;
}
/**
* Returns the {@link IPatternConfiguration} instance used within this
* pattern or <code>null</code> in case none is used.
*
* @return The patternConfiguration instance or <code>null</code> it there
* is none set
*/
protected IPatternConfiguration getPatternConfiguration() {
return patternConfiguration;
}
/**
* Clients can override to complete the {@link IDirectEditingInfo} info.
* This information is needed to switch automatically into the direct
* editing mode. (e.g. after creation of a new object).
*
* @param info
* The direct editing info
* @param bo
* The domain object
*/
public void completeInfo(IDirectEditingInfo info, Object bo) {
}
/**
* Clients can override to complete the {@link IDirectEditingInfo} info.
* This information is needed to switch automatically into the direct
* editing mode. (e.g. after creation of a new object)
*
* @param info
* The direct editing info
* @param bo
* The domain object
* @param keyProperty
* The key property
*/
public void completeInfo(IDirectEditingInfo info, Object bo, String keyProperty) {
}
/**
* Clients may override to modify the resize behavior. The default
* implementation returns a new instance of
* {@link DefaultResizeConfiguration}, which allows bothe the horizontal and
* vertical resize of a shape.
*
* @param context
* Context object holding information about the shape to be
* resized.
* @return An instance of {@link IResizeConfiguration} defining the resize
* behavior.
*/
public IResizeConfiguration getResizeConfiguration(IResizeShapeContext context) {
return new DefaultResizeConfiguration();
}
/**
* Creates the {@link IDeleteFeature} instance that handles the deletion of
* business objects and diagram elements. The default implementation just
* creates an adapted {@link DefaultDeleteFeature}. Concrete pattern
* implementations may either override this method to provide their own
* subclass of {@link DefaultDeleteFeature} or override and extend the
* individual methods provided by {@link IDelete}.
* <p>
* The difference of the delete feature returned here to the standard
* {@link DefaultDeleteFeature} is simply that the instance returned here
* cares about the delegation to the pattern's
* {@link #preDelete(IDeleteContext)}, {@link #isDeleteAbort()} and
* {@link #postDelete(IDeleteContext)} methods. Clients overriding this
* method should re-implement that pattern, in case the delegation is
* desired.
*
* @param context
* the deletion context
* @return the {@link IDeleteFeature} instance to use for this pattern
* @see #canDelete(IDeleteContext)
* @see #preDelete(IDeleteContext)
* @see #isDeleteAbort()
* @see #delete(IDeleteContext)
* @see #postDelete(IDeleteContext)
*/
protected IDeleteFeature createDeleteFeature(IDeleteContext context) {
return new DefaultDeleteFeature(getFeatureProvider()) {
@Override
public void preDelete(IDeleteContext context) {
super.preDelete(context);
AbstractPattern.this.preDelete(context);
}
@Override
public boolean isDeleteAbort() {
return AbstractPattern.this.isDeleteAbort();
}
@Override
public void postDelete(IDeleteContext context) {
AbstractPattern.this.postDelete(context);
super.postDelete(context);
}
};
}
/**
* Clients can override to modify the default behavior if the pattern can
* (and wants to) handle a delete request. The default implementation calls
* {@link #createDeleteFeature(IDeleteContext)} and asks the result's
* canDelete method.
*
* @param context
* The context describing the delete request
*
* @return <code>true</code>, if the pattern can perform the delete
* operation, <code>false</code> otherwise
*/
public boolean canDelete(IDeleteContext context) {
if (wrappedDeleteFeature == null) {
wrappedDeleteFeature = createDeleteFeature(context);
}
return ((wrappedDeleteFeature != null) && wrappedDeleteFeature.canDelete(context));
}
/**
* Clients can override to add actions before the default delete behavior is
* triggered. The default implementation does nothing and is called from the
* registered delete feature.
*
* @param context
* The context describing the delete request
*/
public void preDelete(IDeleteContext context) {
}
/**
* Clients can override to modify the default delete behavior. The default
* implementation calls {@link #createDeleteFeature(IDeleteContext)} and
* triggers the result's delete method.
*
* @param context
* The context describing the delete request
*/
public void delete(IDeleteContext context) {
if (wrappedDeleteFeature == null) {
wrappedDeleteFeature = createDeleteFeature(context);
}
if (wrappedDeleteFeature != null) {
wrappedDeleteFeature.delete(context);
}
}
/**
* Clients can override to add actions after the default delete behavior is
* triggered. The default implementation does nothing and is called from the
* registered delete feature.
*
* @param context
* The context describing the delete request
*/
public void postDelete(IDeleteContext context) {
}
/**
* Creates the {@link IRemoveFeature} instance that handles the removal of
* diagram elements. The default implementation just creates an adapted
* {@link DefaultRemoveFeature}. Concrete pattern implementations may either
* override this method to provide their own subclass of
* {@link DefaultRemoveFeature} or override and extend the individual
* methods provided by {@link IRemove}.
* <p>
* The difference of the remove feature returned here to the standard
* {@link DefaultRemoveFeature} is simply that the instance returned here
* cares about the delegation to the pattern's
* {@link #preRemove(IRemoveContext)}, {@link #isRemoveAbort()} and
* {@link #postRemove(IRemoveContext)} methods. Clients overriding this
* method should re-implement that pattern, in case the delegation is
* desired.
*
* @param context
* the removal context
* @return the {@link IRemoveFeature} instance to use for this pattern
* @see #canRemove(IRemoveContext)
* @see #preRemove(IRemoveContext)
* @see #isRemoveAbort()
* @see #remove(IRemoveContext)
* @see #postRemove(IRemoveContext)
*/
protected IRemoveFeature createRemoveFeature(IRemoveContext context) {
return new DefaultRemoveFeature(getFeatureProvider()) {
@Override
public void preRemove(IRemoveContext context) {
super.preRemove(context);
AbstractPattern.this.preRemove(context);
}
@Override
public boolean isRemoveAbort() {
return AbstractPattern.this.isRemoveAbort();
}
@Override
public void postRemove(IRemoveContext context) {
AbstractPattern.this.postRemove(context);
super.postRemove(context);
}
};
}
/**
* Clients can override to modify the default behavior if the pattern can
* (and wants to) handle a remove request. The default implementation calls
* {@link #createRemoveFeature(IRemoveContext)} and asks the result's
* canRemove method.
*
* @param context
* The context describing the remove request
*
* @return <code>true</code>, if the pattern can perform the delete
* operation, <code>false</code> otherwise
*/
public boolean canRemove(IRemoveContext context) {
if (wrappedRemoveFeature == null) {
wrappedRemoveFeature = createRemoveFeature(context);
}
return wrappedRemoveFeature.canRemove(context);
}
/**
* Clients can override to add actions before the default remove behavior is
* triggered. The default implementation does nothing and is called from the
* registered remove feature.
*
* @param context
* The context describing the remove request
*/
public void preRemove(IRemoveContext context) {
}
/**
* Clients can override to modify the default remove behavior. The default
* implementation calls {@link #createRemoveFeature(IRemoveContext)} and
* triggers the result's remove method.
*
* @param context
* The context describing the remove request
*/
public void remove(IRemoveContext context) {
if (wrappedRemoveFeature == null) {
wrappedRemoveFeature = createRemoveFeature(context);
}
wrappedRemoveFeature.remove(context);
}
/**
* Clients can override to add actions after the default remove behavior is
* triggered. The default implementation does nothing and is called from the
* registered remove feature.
*
* @param context
* The context describing the remove request
*/
public void postRemove(IRemoveContext context) {
}
/**
* Clients can override this method to indicate that the pattern allows
* direct editing for the shape described in the passed
* {@link IDirectEditingContext}. Corresponds to the method
* {@link AbstractDirectEditingFeature#canDirectEdit(IDirectEditingContext)}
* . The default implementation simply returns <code>false</code>.
*
* @param context
* A context object describing the direct edit request.
* @return <code>true</code> in case direct editing shall be allowed,
* <code>false</code> otherwise.
*/
public boolean canDirectEdit(IDirectEditingContext context) {
return false;
}
/**
* This method will be called by the framework to check if the passed String
* is valid as new value for the shape. This method's response time should
* be small since the method is queried after each change of the value in
* the direct edit UI. The default implementation simply returns null to
* indicate that all values are valid. In case of a not valid value, the
* returned string shall indicate the reason why the value is not valid.
* Corresponds to the method
* {@link AbstractDirectEditingFeature#checkValueValid(String, IDirectEditingContext)}
* .
*
* @param value
* The new value to check
* @param context
* A context object describing the direct edit request.
* @return <code>null</code> in case of a valid value, a string describing
* the reason for being not valid otherwise.
*/
public String checkValueValid(String value, IDirectEditingContext context) {
return null;
}
/**
* Can be overridden by clients to define completion functionality for
* direct editing. Corresponds to
* {@link AbstractDirectEditingFeature#completeValue(String, int, String, IDirectEditingContext)}
* . The default implementation simply returns the parameter chosenValue.
*
* @param value
* The current value
* @param caretPosition
* The current cursor position
* @param choosenValue
* The value chosen by user
* @param context
* A context object describing the direct edit request.
* @return The new value
*/
public String completeValue(String value, int caretPos, String chosenValue, IDirectEditingContext context) {
return chosenValue;
}
/**
* This value will be used if the cell editor is a combo box. This
* functionality only applies to TYPE_DROPDOWN. Corresponds to the method
* {@link AbstractDirectEditingFeature#getPossibleValues(IDirectEditingContext)}
* . The default implementation returns an empty string array.
*
* @param context
* A context object describing the direct edit request.
* @return The possible values for the combo box.
*/
public String[] getPossibleValues(IDirectEditingContext context) {
return EMPTY_STRING_ARRAY;
}
/**
* This proposals will be used for the completion list of a simple text cell
* editor. This functionality only applies to TYPE_TEXT. Corresponds to the
* method
* {@link AbstractDirectEditingFeature#getValueProposals(String, int, IDirectEditingContext)}
* . The default implementation returns an empty string array.
*
* @param value
* The current value
* @param caretPosition
* The current cursor position
* @param context
* A context object describing the direct edit request.
* @return The proposed values
*/
public String[] getValueProposals(String value, int caretPos, IDirectEditingContext context) {
return EMPTY_STRING_ARRAY;
}
/**
* Checks if auto completion is enabled. This functionality only applies to
* TYPE_TEXT. Corresponds to method
* {@link AbstractDirectEditingFeature#isAutoCompletionEnabled()}. The
* default implementation simply returns <code>false</code>.
*
* @return <code>true</code>, if proposals should appear automatically,
* <code>false</code> otherwise.
*/
public boolean isAutoCompletionEnabled() {
return false;
}
/**
* Checks if completion is available. This functionality only applies to
* TYPE_TEXT. Corresponds to method
* {@link AbstractDirectEditingFeature#isCompletionAvailable()}. The default
* implementation simply returns <code>false</code>.
*
* @return <code>true</code> if completion is / proposals are available at
* all, <code>false</code> otherwise.
*/
public boolean isCompletionAvailable() {
return false;
}
/**
* Defines if the input field should be streched to fit its contents. This
* functionality applies to TYPE_TEXT, TYPE_DROPDOWN and
* TYPE_DROPDOWN_READ_ONLY. Corresponds to method
* {@link AbstractDirectEditingFeature#stretchFieldToFitText()}. The default
* implementation simply returns <code>false</code>.
*
* @return <code>true</code> if the field should exactly fit the contents,
* <code>false</code> otherwise.
*/
public boolean stretchFieldToFitText() {
return false;
}
/**
* The Graphiti framework calls this method to decide which UI to show up
* for direct editing. Corresponds to the method
* {@link AbstractDirectEditingFeature#getEditingType()}. The default
* implementation return {@link IDirectEditing#TYPE_NONE}, other valid type
* are defined by the TYPE_* constants in {@link IDirectEditing}.
*
* @return The desired editing type
*/
public int getEditingType() {
return IDirectEditing.TYPE_NONE;
}
/**
* Provides the initial value for display in the newly opened text editing
* UI component. Corresponds to the method
* {@link AbstractDirectEditingFeature#getInitialValue(IDirectEditingContext)}
* . The default implementation always returns an empty string.
*
* @param context
* A context object describing the direct edit request.
* @return The initial string value to be displayed for editing by the user.
*/
public String getInitialValue(IDirectEditingContext context) {
return ""; //$NON-NLS-1$
}
/**
* Set the new value after direct editing is finished. The value comes from
* the text editing UI component. Corresponds to the method
* {@link AbstractDirectEditingFeature#setValue(String, IDirectEditingContext)}
* . The default implementation does nothing.
*
* @param value
* The new value to be set
* @param context
* A context object describing the direct edit request.
*/
public void setValue(String value, IDirectEditingContext context) {
}
/**
* The direct editing mode contains controls for code completion and the
* selection from a combo box. In both cases the standard implementation
* supports only strings.
* <p>
* If the client wants to work with Objects he must provide an
* implementation of {@link IProposalSupport}. In this case the following
* methods of the pattern are ignored:
* <p>
* <code>
* <br>* String checkValueValid(String value, IDirectEditingContext context);
* <br>* String completeValue(String value, int caretPosition, String choosenValue, IDirectEditingContext context);
* <br>* String[] getPossibleValues(IDirectEditingContext context);
* <br>* String[] getValueProposals(String value, int caretPosition, IDirectEditingContext context);
* <br>* void setValue(String value, IDirectEditingContext context);
* </code><br>
* Corresponds to the method
* {@link AbstractDirectEditingFeature#getProposalSupport()}. The default
* implementation returns <code>null</code> to enable the standard
* string-based direct editing functionality.
*
* @return The special implementation to support Objects in code completion
* and combo box
* @since 0.8
*/
public IProposalSupport getProposalSupport() {
return null;
}
/**
* Is queried by the framework after a pattern has been executed to find out
* if this pattern should appear in the undo stack. By default all patterns
* should appear there (see implementation in AbstractPattern), but single
* pattern functionality may decide to override this behavior. Note that
* this is a dynamic attribute of the pattern that is queried each time
* <b>after</b> the pattern functionality has been executed.
* <p>
* <b>IMPORTANT NOTE:</b> The implementor of the feature is responsible for
* correctly implementing this method! It will lead to inconsistencies if
* this method returns <code>false</code> although the pattern did changes.
*
* @param actionType
* the followings types are currently supported:
* <code>IDelete.class, IRemove.class</code>
*
*
* @return <code>true</code> if the last action of the pattern from this
* action type should appear in the undo stack, <code>false</code>
* otherwise
*
* @since 0.9
*/
public boolean hasDoneChanges(Class<?> actionType) {
boolean ret = true;
if (IDelete.class.equals(actionType)) {
if (wrappedDeleteFeature != null) {
ret = wrappedDeleteFeature.hasDoneChanges();
}
} else if (IRemove.class.equals(actionType)) {
if (wrappedRemoveFeature != null) {
ret = wrappedRemoveFeature.hasDoneChanges();
}
}
return ret;
}
/**
* @since 0.12
*/
@Override
public boolean isDeleteAbort() {
return false;
}
/**
* @since 0.12
*/
@Override
public boolean isRemoveAbort() {
return false;
}
}