blob: eef290b96a9445292631937ca30ff13a23bbfade [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.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);
}
}
}