/******************************************************************************* * 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); } } }