blob: cdfca0030354c6a4721182f329196dc6ca82dc8c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.bpel.ui.editparts;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.bpel.model.Activity;
import org.eclipse.bpel.model.Sequence;
import org.eclipse.bpel.ui.BPELUIPlugin;
import org.eclipse.bpel.ui.IHoverHelper;
import org.eclipse.bpel.ui.IHoverHelperSupport;
import org.eclipse.bpel.ui.adapters.AdapterNotification;
import org.eclipse.bpel.ui.adapters.IContainer;
import org.eclipse.bpel.ui.editparts.policies.BPELComponentEditPolicy;
import org.eclipse.bpel.ui.editparts.policies.BPELDirectEditPolicy;
import org.eclipse.bpel.ui.editparts.policies.BPELOrderedLayoutEditPolicy;
import org.eclipse.bpel.ui.extensions.BPELUIRegistry;
import org.eclipse.bpel.ui.figures.CenteredConnectionAnchor;
import org.eclipse.bpel.ui.util.BPELCellEditorLocator;
import org.eclipse.bpel.ui.util.BPELDirectEditManager;
import org.eclipse.bpel.ui.util.BPELDragEditPartsTracker;
import org.eclipse.bpel.ui.util.BPELUtil;
import org.eclipse.bpel.ui.util.ModelHelper;
import org.eclipse.bpel.ui.util.MultiObjectAdapter;
import org.eclipse.bpel.ui.util.marker.BPELEditPartMarkerDecorator;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.AccessibleAnchorProvider;
import org.eclipse.gef.AccessibleEditPart;
import org.eclipse.gef.DragTracker;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.tools.DirectEditManager;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.viewers.TextCellEditor;
/**
* The superclass of all BPEL edit parts. Provides a listener on the model
* which calls the handleModelChanged() method.
*/
@SuppressWarnings("nls")
public abstract class BPELEditPart extends AbstractGraphicalEditPart implements IHoverHelperSupport {
/**
* The amount of spacing to place between child items. Subclasses
*/
public static final int SPACING = 14;
static protected String EMPTY_STRING = "";
protected AccessibleEditPart acc;
protected MultiObjectAdapter adapter;
protected BPELEditPartMarkerDecorator.MarkerMotionListener markerMotionListener;
// The direct edit manager handles the in-place editing of node names on the graphical canvas.
private DirectEditManager manager;
// Mouse motion hover help support
protected int mouseLocation = 0; // 0 == no marker, 1 == top drawer, 2 == bottom drawer, 3 == marker on main figure
/**
* Create a new BPELEditPart.
*
* Construct the model adapter.
*/
public BPELEditPart() {
adapter = new MultiObjectAdapter() {
@Override
public void notify(Notification n) {
int eventGroup = n.getEventType() / 100;
if (eventGroup == AdapterNotification.NOTIFICATION_MARKERS_CHANGED_GROUP ) {
refreshVisuals();
return ;
}
// TODO: check if we care about this notification
if (isActive()) {
handleModelChanged();
}
refreshAdapters();
}
};
}
/**
* Default implementation based on IContainer. Should be sufficient except in
* special cases (such as ProcessEditPart?).
*/
@Override
protected List getModelChildren() {
IContainer container = BPELUtil.adapt(getModel(), IContainer.class);
if (container != null) {
return container.getChildren(getModel());
}
return super.getModelChildren();
}
protected void addAllAdapters() {
EObject modelObject = (EObject)getModel();
adapter.addToObject(modelObject);
// if the object has an extension, add adapter to that too
EObject extension = ModelHelper.getExtension(modelObject);
if (extension != null) adapter.addToObject(extension);
// if the object contains an implicit sequence, add adapter to that also
try {
Activity activity = ModelHelper.getActivity(modelObject);
if (activity instanceof Sequence) {
// TODO: perhaps we should check and make sure it's an implicit sequence!
// for now, don't worry about it.
adapter.addToObject(activity);
}
} catch (IllegalArgumentException e) {
// it's not a single-activity container. ignore.
}
// ..but we probably don't need an adapter on the implicit sequence...
}
protected void removeAllAdapters() {
adapter.removeFromAll();
}
protected void refreshAdapters() {
removeAllAdapters();
addAllAdapters();
}
/**
* @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#activate()
*/
@Override
public void activate() {
if (isActive()) {
return;
}
super.activate();
addAllAdapters();
}
/**
* @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#deactivate()
*/
@Override
public void deactivate() {
if (!isActive()) {
return;
}
removeAllAdapters();
super.deactivate();
clearConnections();
}
@Override
protected void createEditPolicies() {
// The COMPONENT_ROLE policy determines how an edit part is deleted.
installEditPolicy(EditPolicy.COMPONENT_ROLE, new BPELComponentEditPolicy());
// The DIRECT_EDIT_ROLE policy determines how in-place editing takes place.
installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new BPELDirectEditPolicy());
}
/**
* The model has changed. Perform any actions necessary to ensure that the
* edit part, model and graphical representation are in sync.
*
* Subclasses may override but should call super.
*/
protected void handleModelChanged() {
// If property name is children, refresh children.
// If property name is size or location, refresh visuals.
// TODO: refresh connections in there somewhere too!
refreshChildren();
refreshVisuals();
refresh();
}
/**
* Get an anchor at the given location for this edit part.
*
* This must be called after the figure for this edit part has been created.
* @param location
* @return
*/
public ConnectionAnchor getConnectionAnchor(int location) {
return new CenteredConnectionAnchor(getFigure(), location, 0);
}
/**
* Return whether or not the edit part can execute the given request.
* The answer is determined by asking each edit policy and returning
* true if any policy can execute the request.
* @param request
* @return
*/
public boolean canExecuteRequest(Request request) {
EditPolicyIterator i = getEditPolicyIterator();
while (i.hasNext()) {
Command cm= i.next().getCommand(request);
if (cm != null)
if (cm.canExecute())
return true;
}
return false;
}
/**
* Override to handle direct edit requests
*/
@Override
public void performRequest(Request request) {
if (request.getType() == RequestConstants.REQ_DIRECT_EDIT) {
performDirectEdit();
} else {
super.performRequest(request);
}
}
/**
* Return whether the receiver can perform direct edit.
*/
public boolean canPerformDirectEdit() {
return getLabelFigure() != null;
}
/**
* Invoke direct edit on the receiver
*/
public void performDirectEdit() {
if (getLabelFigure() != null) {
// make sure we can execute it through
Command cmd = BPELDirectEditPolicy.getFinalizeCommand(this.getModel(), "blahblah"); //$NON-NLS-1$
if (cmd == null || !cmd.canExecute())
return;
if (manager == null) {
manager = new BPELDirectEditManager(this, TextCellEditor.class,
new BPELCellEditorLocator(getLabelFigure()), getLabelValidator());
}
manager.show();
}
}
/**
* Return the label that should be used for direct edit.
*
* The default implementation returns null.
*
* Subclasses should override to return the appropriate label.
*/
public Label getLabelFigure() {
return null;
}
/**
* Return the text to display in the label for the edit part.
*
* The default implementation returns null.
*
* Subclasses should override to return the appropriate text.
*/
public String getLabelContent() {
return null;
}
/**
* Set the text to display in the label for the edit part.
*
* The default implementation does nothing.
*
* Subclasses should override to set the label to the given string.
*/
public void setLabelContent(String str) {
}
/**
* TODO: visibility increased (from protected) so I could move the edit
* policies to a separate package. In future, we should change it back.
*/
@Override
public void refreshVisuals() {
super.refreshVisuals();
refreshHoverHelp();
}
@Override
protected void refreshChildren() {
super.refreshChildren();
// Not the most logical place to refresh connections, but it
// needs to be refreshed after the children are refreshed
//TODO: May make more sense to put connections into adapter
// so connections are not refreshed unnecessarily
refreshConnections();
}
protected void refreshConnections(){
EditPolicy policy = getEditPolicy(EditPolicy.LAYOUT_ROLE);
if (policy instanceof BPELOrderedLayoutEditPolicy){
((BPELOrderedLayoutEditPolicy)policy).refreshConnections();
}
}
protected void clearConnections(){
EditPolicy policy = getEditPolicy(EditPolicy.LAYOUT_ROLE);
if (policy instanceof BPELOrderedLayoutEditPolicy){
((BPELOrderedLayoutEditPolicy)policy).clearConnections();
}
}
@Override
public Object getAdapter(Class key) {
if (key.isInstance(getModel())) {
return getModel();
}
if (key == AccessibleAnchorProvider.class) {
return new DefaultAccessibleAnchorProvider() {
private List<Point> getDefaultLocations() {
List<Point> list = new ArrayList<Point>();
Rectangle r = getFigure().getBounds();
// when calculating the target in connection tools, it was targettig
// the object behind the figure we were interested in, so adding an addition
// fudge factor to -2 helps make sure we hit the right target
Point p = r.getTopRight().translate(-r.width / 2, r.height / 3);
getFigure().translateToAbsolute(p);
list.add(p);
return list;
}
/**
* @see AccessibleAnchorProvider#getSourceAnchorLocations()
*/
@Override
public List<Point> getSourceAnchorLocations() {
return getDefaultLocations();
}
/**
* @see AccessibleAnchorProvider#getTargetAnchorLocations()
*/
@Override
public List<Point> getTargetAnchorLocations() {
return getDefaultLocations();
}
};
}
return super.getAdapter(key);
}
public void regenerateVisuals() {
}
// increase visibility!
@Override
public void refreshSourceConnections() { super.refreshSourceConnections(); }
@Override
public void refreshTargetConnections() { super.refreshTargetConnections(); }
public void refreshHoverHelp() {
// Refresh the tooltip if we can find a helper.
try {
IHoverHelper helper = BPELUIRegistry.getInstance().getHoverHelper();
if (helper != null) {
IFigure tooltip = helper.getHoverFigure((EObject)getModel());
getFigure().setToolTip(tooltip);
}
} catch (CoreException e) {
getFigure().setToolTip(null);
BPELUIPlugin.log(e);
}
}
@Override
protected IFigure createFigure() {
return null;
}
@Override
protected AccessibleEditPart getAccessibleEditPart() {
if (acc == null) acc = createAccessible();
return acc;
}
protected AccessibleEditPart createAccessible() {
return BPELUtil.getAccessibleEditPart(this);
}
protected BPELEditPartMarkerDecorator.MarkerMotionListener getMarkerMotionListener() {
return markerMotionListener = new BPELEditPartMarkerDecorator.MarkerMotionListener() {
public void markerEntered(IMarker marker) {
// refresh the hover help for this marker.
mouseLocation = 3;
refreshHoverHelp();
}
};
}
@Override
public DragTracker getDragTracker(Request request) {
return new BPELDragEditPartsTracker(this);
}
/**
* Returns the label validator for this part or <code>null</code> if not applicable. The
* default behavior is to return <code>null</code>. Subclasses may override this method.
*/
protected IInputValidator getLabelValidator() {
return null;
}
}