/******************************************************************************* | |
* 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 2.0 | |
* which accompanies this distribution, and 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.bpel.ui.editparts; | |
import java.util.Collections; | |
import java.util.List; | |
import org.eclipse.bpel.common.ui.markers.IModelMarkerConstants; | |
import org.eclipse.bpel.model.Link; | |
import org.eclipse.bpel.ui.BPELUIPlugin; | |
import org.eclipse.bpel.ui.IBPELUIConstants; | |
import org.eclipse.bpel.ui.adapters.ILabeledElement; | |
import org.eclipse.bpel.ui.adapters.IMarkerHolder; | |
import org.eclipse.bpel.ui.editparts.borders.ContainerBorder; | |
import org.eclipse.bpel.ui.editparts.borders.DrawerBorder; | |
import org.eclipse.bpel.ui.editparts.borders.LeafBorder; | |
import org.eclipse.bpel.ui.editparts.figures.CollapsableContainerFigure; | |
import org.eclipse.bpel.ui.editparts.util.BPELDecorationLayout; | |
import org.eclipse.bpel.ui.figures.CenteredConnectionAnchor; | |
import org.eclipse.bpel.ui.figures.ILayoutAware; | |
import org.eclipse.bpel.ui.util.BPELDragEditPartsTracker; | |
import org.eclipse.bpel.ui.util.BPELUtil; | |
import org.eclipse.bpel.ui.util.marker.BPELEditPartMarkerDecorator; | |
import org.eclipse.core.resources.IMarker; | |
import org.eclipse.draw2d.Border; | |
import org.eclipse.draw2d.ConnectionAnchor; | |
import org.eclipse.draw2d.FlowLayout; | |
import org.eclipse.draw2d.IFigure; | |
import org.eclipse.draw2d.Label; | |
import org.eclipse.draw2d.Layer; | |
import org.eclipse.draw2d.PositionConstants; | |
import org.eclipse.draw2d.geometry.Dimension; | |
import org.eclipse.draw2d.geometry.Point; | |
import org.eclipse.draw2d.geometry.Rectangle; | |
import org.eclipse.emf.common.notify.Adapter; | |
import org.eclipse.emf.ecore.EObject; | |
import org.eclipse.gef.DragTracker; | |
import org.eclipse.gef.Request; | |
import org.eclipse.swt.graphics.Image; | |
/** | |
* Subclasses of CollapsableEditPart represent BPEL activities which | |
* can be graphically represented in an expanded or collapsed manner. | |
* | |
* In the collapsed state, this class will render the node. In the | |
* expanded state, the subclass will be expected to render the node. | |
*/ | |
public abstract class CollapsableEditPart extends CompositeActivityEditPart implements ILayoutAware { | |
// Whether or not the edit part is collapsed | |
protected boolean collapsed; | |
// The primary image for the edit part | |
protected Image image; | |
// The images in the top and bottom drawer, if any | |
protected Image topImage, bottomImage; | |
// The label to display when collapsed. Subclasses may also use it | |
// when expanded if desired. | |
protected Label collapsedLabel; | |
// Marker support | |
protected IFigure contentFigure; | |
protected BPELEditPartMarkerDecorator editPartMarkerDecorator; | |
protected Adapter childrenAdapter; | |
public class CollapsableDecorationLayout extends BPELDecorationLayout { | |
private int borderImageWidth; | |
public CollapsableDecorationLayout(int borderImageWidth) { | |
this.borderImageWidth = borderImageWidth; | |
} | |
@Override | |
protected Point calculateLocation(int locationHint, IFigure container, Dimension childDimension) { | |
Rectangle area = container.getClientArea(); | |
switch (locationHint) { | |
case PositionConstants.CENTER: | |
// Center | |
return new Point(area.x + area.width / 2 - childDimension.width / 2, area.y + area.height / 2 - childDimension.height / 2); | |
case PositionConstants.TOP: | |
// Top Center | |
return new Point(area.x + area.width / 2 - childDimension.width / 2, area.y + borderImageWidth / 2); | |
case PositionConstants.BOTTOM: | |
// Bottom Center | |
return new Point(area.x + area.width / 2 - childDimension.width / 2, area.y + area.height - childDimension.height); | |
case PositionConstants.LEFT: { | |
// Center Left | |
int x = area.x + DrawerBorder.DRAWER_WIDTH; | |
int y = area.y + (area.height / 2) - (childDimension.width / 2); | |
if (!isCollapsed()) { | |
y += DrawerBorder.DRAWER_HALF_HEIGHT / 2; | |
} | |
return new Point(x, y); | |
} | |
case PositionConstants.RIGHT: { | |
// Center Right | |
int x = area.x + area.width - childDimension.width - DrawerBorder.DRAWER_WIDTH; | |
int y = area.y + (area.height / 2) - (childDimension.width / 2); | |
if (!isCollapsed()) { | |
y += DrawerBorder.DRAWER_HALF_HEIGHT / 2; | |
} | |
return new Point(x, y); | |
} | |
case PositionConstants.TOP | PositionConstants.LEFT: { | |
// Top Left | |
int x = area.x + DrawerBorder.DRAWER_WIDTH; | |
int y = area.y + (borderImageWidth / 2); | |
if (!isCollapsed()) { | |
y += (collapsedLabel.getSize().height / 2) - (IBPELUIConstants.ARC_WIDTH / 2); | |
} | |
return new Point(x, y); | |
} | |
case PositionConstants.TOP | PositionConstants.RIGHT: { | |
// Top Right | |
int x = area.x + area.width - childDimension.width - DrawerBorder.DRAWER_WIDTH; | |
int y = area.y + (borderImageWidth / 2); | |
if (!isCollapsed()) { | |
y += (collapsedLabel.getSize().height / 2) - (IBPELUIConstants.ARC_WIDTH / 2); | |
} else { | |
x -= 1; // 1 is a magic number | |
} | |
return new Point(x, y); | |
} | |
case PositionConstants.BOTTOM | PositionConstants.LEFT: { | |
// Bottom Left | |
int x = area.x + DrawerBorder.DRAWER_WIDTH; | |
int y = area.y + area.height - IBPELUIConstants.ARC_WIDTH; | |
if (!isCollapsed()) { | |
y -= DrawerBorder.DRAWER_HALF_HEIGHT / 2; | |
} else { | |
y -= DrawerBorder.DRAWER_HALF_HEIGHT; | |
} | |
return new Point(x, y); | |
} | |
case PositionConstants.BOTTOM | PositionConstants.RIGHT: { | |
// Bottom Right | |
int x = area.x + area.width - childDimension.width - DrawerBorder.DRAWER_WIDTH; | |
int y = area.y + area.height - IBPELUIConstants.ARC_WIDTH; | |
if (!isCollapsed()) { | |
y -= DrawerBorder.DRAWER_HALF_HEIGHT / 2; | |
} else { | |
y -= DrawerBorder.DRAWER_HALF_HEIGHT; | |
x -= 1; // 1 is a magic number | |
} | |
return new Point(x, y); | |
} | |
default: | |
return new Point(area.x, area.y); | |
} | |
} | |
} | |
public CollapsableEditPart() { | |
this(false); | |
} | |
public CollapsableEditPart(boolean collapsed) { | |
this.collapsed = collapsed; | |
} | |
protected void initializeLabels() { | |
this.collapsedLabel = new Label(getLabel()); | |
// Get Image from registry | |
this.image = getImg(); | |
} | |
private boolean isGenericContainerBorder() { | |
Border border = this.getContentPane().getBorder(); | |
return (border != null && border instanceof ContainerBorder); | |
} | |
@Override | |
protected DrawerBorder getDrawerBorder() { | |
Border border = getContentPane().getBorder(); | |
if (border instanceof DrawerBorder) return (DrawerBorder)border; | |
return null; | |
} | |
@Override | |
public Label getLabelFigure() { | |
if (isGenericContainerBorder()) { | |
ContainerBorder border = (ContainerBorder)(this.getContentPane().getBorder()); | |
return border.getLabel(); | |
} | |
return collapsedLabel; | |
} | |
@Override | |
protected IFigure createFigure() { | |
initializeLabels(); | |
editPartMarkerDecorator = new BPELEditPartMarkerDecorator( | |
(EObject)getModel(), | |
new CollapsableDecorationLayout(image.getBounds().width) | |
); | |
editPartMarkerDecorator.addMarkerMotionListener(getMarkerMotionListener()); | |
IFigure figure = getNewContentPane(editPartMarkerDecorator.getDecorationLayer()); | |
this.contentFigure = figure; | |
figure.setForegroundColor(BPELUIPlugin.INSTANCE.getColorRegistry().get(IBPELUIConstants.COLOR_BLACK)); | |
if (isCollapsed()) { | |
addCollapsedContents(figure); | |
} else { | |
configureExpandedFigure(figure); | |
} | |
return editPartMarkerDecorator.createFigure(figure); | |
} | |
protected IFigure getNewContentPane(Layer layer) { | |
CollapsableContainerFigure fig = new CollapsableContainerFigure(getModel(), image, getLabel()); | |
fig.addMouseMotionListener(getMouseMotionListener()); | |
fig.setEditPart(this); | |
return fig; | |
} | |
protected CollapsableContainerFigure getContentFigure() { | |
return (CollapsableContainerFigure)this.contentFigure; | |
} | |
// subclasses can override this to change edit policies, etc. | |
protected void notifyCollapsed(boolean collapsed) { } | |
public void setCollapsed(boolean collapsed) { | |
if (!isCollapsable()) return; | |
if (isCollapsed() == collapsed) return; | |
this.collapsed = collapsed; | |
if (isGenericContainerBorder()) | |
getContentFigure().setCollapsed(collapsed); | |
notifyCollapsed(collapsed); | |
IFigure figure = getContentPane(); | |
if (isCollapsed()) { | |
// First refresh children, which removes all model children's | |
// figures | |
refreshChildren(); | |
// changed by Grid.Qian | |
// when use figure.remove() to remove the children element | |
// we will change the list of figure.getChildren() | |
// so we will get the a error. | |
int size = figure.getChildren().size(); | |
for(int i = 0; i < size; i++){ | |
figure.remove((IFigure)figure.getChildren().get(0)); | |
} | |
/*// Manually remove the rest of the children | |
for (Object o : figure.getChildren()) { | |
figure.remove((IFigure)o); | |
}*/ | |
// Now restore the collapsed children, border and layout | |
addCollapsedContents(figure); | |
} else { | |
// changed by Grid.Qian | |
// when use figure.remove() to remove the children element | |
// we will change the list of figure.getChildren() | |
// so we will get the a error. | |
int size = figure.getChildren().size(); | |
for(int i = 0; i < size; i++){ | |
figure.remove((IFigure)figure.getChildren().get(0)); | |
} | |
/*// Manually remove the children | |
for (Object o : figure.getChildren()) { | |
figure.remove((IFigure)o); | |
}*/ | |
// Now restore the expanded children, border and layout | |
configureExpandedFigure(figure); | |
refreshChildren(); | |
} | |
refreshSourceConnections(); | |
refreshTargetConnections(); | |
// Switching collapsed states may have changed the border, which is | |
// responsible for drawing the drawer markers. Refresh these markers | |
// now. | |
refreshDrawerImages(); | |
// Force a repaint, as the drawer images may have changed. | |
getFigure().repaint(); | |
} | |
public boolean isCollapsed() { | |
if (isGenericContainerBorder()) | |
return getContentFigure().isCollapsed(); | |
return collapsed; | |
} | |
protected boolean isCollapsable() { | |
return true; | |
} | |
@Override | |
protected void unregisterVisuals() { | |
this.image = null; | |
this.editPartMarkerDecorator = null; | |
this.topImage = null; | |
this.bottomImage = null; | |
super.unregisterVisuals(); | |
} | |
protected void addCollapsedContents(IFigure figure) { | |
figure.setLayoutManager(new FlowLayout()); | |
if (!isGenericContainerBorder()) { | |
LeafBorder lBorder = new LeafBorder(figure); | |
// FIX: We have to set the editpart! | |
lBorder.setEditPart(this); | |
figure.setBorder(lBorder); | |
figure.addMouseMotionListener(getMouseMotionListener()); | |
figure.add(collapsedLabel); | |
} | |
} | |
@Override | |
protected List getModelChildren() { | |
if (isCollapsed()) return Collections.EMPTY_LIST; | |
return getExpandedChildren(); | |
} | |
protected boolean isPointInCollapseIcon(Point point) { | |
if (isGenericContainerBorder()) { | |
return getContentFigure().isPointInCollapseImage(point.x, point.y); | |
} | |
return true; | |
} | |
@Override | |
public DragTracker getDragTracker(Request request) { | |
return new BPELDragEditPartsTracker(this) { | |
@Override | |
protected boolean handleDoubleClick(int button) { | |
if (!isGenericContainerBorder()) | |
setCollapsed(!isCollapsed()); | |
return true; | |
} | |
@Override | |
protected boolean handleButtonDown(int button) { | |
if (isGenericContainerBorder()) { | |
if (isPointInCollapseIcon(getLocation())) { | |
setCollapsed(!isCollapsed()); | |
return true; | |
} | |
} | |
return super.handleButtonDown(button); | |
} | |
}; | |
} | |
/** | |
* @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals() | |
*/ | |
@Override | |
public void refreshVisuals() { | |
super.refreshVisuals(); | |
// The name has changed, change the corresponding label | |
if (!isGenericContainerBorder()) { | |
collapsedLabel.setText(getLabel()); | |
} else { | |
getContentFigure().setName(getLabel()); | |
getContentFigure().revalidate(); | |
} | |
// Refresh any decorations on this edit part | |
editPartMarkerDecorator.refresh(); | |
refreshDrawerImages(); | |
// Force a repaint, as the drawer images may have changed. | |
getFigure().repaint(); | |
} | |
/** | |
* Return the image that was created for the receiver. This image corresponds | |
* to the image descriptor which the subclass provided in getImageDescriptor(). | |
* | |
* This image may be used by subclasses for the lifetime of this edit part. | |
* TODO: Get rid of this. Subclasses should handle the lifecycle. | |
*/ | |
protected Image getImage() { | |
return image; | |
} | |
/** | |
* Return an image which should be displayed in this node while | |
* collapsed. Subclasses may override. | |
*/ | |
protected Image getImg() { | |
ILabeledElement element = BPELUtil.adapt(getActivity(), ILabeledElement.class); | |
if (element != null) { | |
return element.getSmallImage(getActivity()); | |
} | |
return null; | |
} | |
/** | |
* Return a string which should be displayed in this node while collapsed. | |
*/ | |
protected String getLabel() { | |
ILabeledElement element = BPELUtil.adapt(getActivity(), ILabeledElement.class); | |
if (element != null) { | |
return element.getLabel(getActivity()); | |
} | |
return null; | |
} | |
/** | |
* Return a list of the model children that should be displayed while | |
* the node is in expanded mode. Subclasses may override. | |
*/ | |
protected List getExpandedChildren() { | |
return super.getModelChildren(); | |
} | |
/** | |
* Configure the given figure for expanded mode. Subclasses should do work | |
* in here to set border and layout information for expanded mode. | |
*/ | |
protected abstract void configureExpandedFigure(IFigure figure); | |
/** | |
* A CollapsableEditPart has extra responsibilities for source and target connections, | |
* because it must show flow links for any activities which are inside it but not | |
* visible (in the case that the CollapsableEditPart is collapsed). | |
*/ | |
@Override | |
protected List<Link> getModelSourceConnections() { | |
final List<Link> result = super.getModelSourceConnections(); | |
// if (isCollapsed()) { | |
// BPELUtil.visitModelDepthFirst(getActivity(), new IModelVisitor() { | |
// public boolean visit(Object modelObject) { | |
// if (modelObject instanceof Activity) { | |
// List sources = ((Activity)modelObject).getSources(); | |
// Iterator it = sources.iterator(); | |
// while (it.hasNext()) { | |
// Source source = (Source)it.next(); | |
// Target target = source.getLink().getTarget(); | |
// Activity targetActivity = target.getActivity(); | |
// // If the target is not contained in this model object, add the | |
// // connection. | |
// if (!containsModel(targetActivity)) { | |
// result.add(source.getLink()); | |
// } | |
// } | |
// } | |
// return true; | |
// } | |
// }); | |
// } | |
return result; | |
} | |
/** | |
* A CollapsableEditPart has extra responsibilities for source and target connections, | |
* because it must show flow links for any activities which are inside it but not | |
* visible (in the case that the CollapsableEditPart is collapsed). | |
*/ | |
@Override | |
protected List<Link> getModelTargetConnections() { | |
final List<Link> result = super.getModelTargetConnections(); | |
// if (isCollapsed()) { | |
// BPELUtil.visitModelDepthFirst(getActivity(), new IModelVisitor() { | |
// public boolean visit(Object modelObject) { | |
// if (modelObject instanceof Activity) { | |
// List targets = ((Activity)modelObject).getTargets(); | |
// Iterator it = targets.iterator(); | |
// while (it.hasNext()) { | |
// Target target = (Target)it.next(); | |
// Source source = target.getLink().getSource(); | |
// Activity sourceActivity = source.getActivity(); | |
// // If the source is not contained in this model object, add the | |
// // connection. | |
// if (!containsModel(sourceActivity)) { | |
// result.add(target.getLink()); | |
// } | |
// } | |
// } | |
// return true; | |
// } | |
// }); | |
// } | |
return result; | |
} | |
@Override | |
public IFigure getContentPane() { | |
return contentFigure; | |
} | |
public Label getCollapsedLabel() { | |
return collapsedLabel; | |
} | |
protected void refreshDrawerImages() { | |
DrawerBorder border = (DrawerBorder)getContentPane().getBorder(); | |
if (topImage != null) { | |
topImage.dispose(); | |
topImage = null; | |
} | |
if (bottomImage != null) { | |
bottomImage.dispose(); | |
bottomImage = null; | |
} | |
IMarkerHolder holder = BPELUtil.adapt(getActivity(), IMarkerHolder.class); | |
int topMarkerPriority = Integer.MIN_VALUE; | |
int bottomMarkerPriority = Integer.MIN_VALUE; | |
IMarker topMarker = null; | |
IMarker bottomMarker = null; | |
for (IMarker marker : holder.getMarkers(getActivity())) { | |
if (marker.getAttribute(IModelMarkerConstants.DECORATION_MARKER_VISIBLE_ATTR, true) == false) { | |
continue; | |
} | |
String value = marker.getAttribute(IModelMarkerConstants.DECORATION_GRAPHICAL_MARKER_ANCHOR_POINT_ATTR,EMPTY_STRING); | |
if (value.equals(IBPELUIConstants.MARKER_ANCHORPOINT_DRAWER_TOP)) { | |
int priority = marker.getAttribute(IModelMarkerConstants.DECORATION_MARKER_PRIORITY_ATTR, 0); | |
if (priority > topMarkerPriority) { | |
topMarkerPriority = priority; | |
topImage = BPELUtil.getImage(marker); | |
topMarker = marker; | |
} | |
} else if (value.equals(IBPELUIConstants.MARKER_ANCHORPOINT_DRAWER_BOTTOM)) { | |
int priority = marker.getAttribute(IModelMarkerConstants.DECORATION_MARKER_PRIORITY_ATTR, 0); | |
if (priority > bottomMarkerPriority) { | |
bottomMarkerPriority = priority; | |
bottomImage = BPELUtil.getImage(marker); | |
bottomMarker = marker; | |
} | |
} | |
} | |
border.setTopImage(topImage); | |
border.setBottomImage(bottomImage); | |
border.setTopMarker(topMarker); | |
border.setBottomMarker(bottomMarker); | |
} | |
/** | |
* 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. | |
*/ | |
@Override | |
public ConnectionAnchor getConnectionAnchor(int location) { | |
switch(location){ | |
case CenteredConnectionAnchor.TOP_INNER: | |
return new CenteredConnectionAnchor(getFigure(), location, 30); | |
case CenteredConnectionAnchor.LEFT: | |
return new CenteredConnectionAnchor(getFigure(), CenteredConnectionAnchor.LEFT_INNER, 0); | |
case CenteredConnectionAnchor.RIGHT: | |
return new CenteredConnectionAnchor(getFigure(), CenteredConnectionAnchor.RIGHT_INNER, 0); | |
default: | |
return super.getConnectionAnchor(location); | |
} | |
} | |
} |