/******************************************************************************* * Copyright (c) 2005 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.common.ui.decorator; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.bpel.common.ui.Messages; import org.eclipse.bpel.common.ui.layouts.AlignedFlowLayout; import org.eclipse.bpel.common.ui.layouts.FillParentLayout; import org.eclipse.bpel.common.ui.markers.IModelMarkerConstants; import org.eclipse.bpel.common.ui.markers.ModelMarkerUtil; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.CoreException; import org.eclipse.draw2d.AbstractLayout; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.ImageFigure; import org.eclipse.draw2d.Label; import org.eclipse.draw2d.Layer; import org.eclipse.draw2d.LayeredPane; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.ecore.EObject; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.graphics.Image; /** * A class to encapsulate marker support for an <code>EditPart</code>. * The <code>EditPart</code> should hold an instance of this class in a field. * It makes use of the EMF markers support. * * <p> * In its <code>refreshVisuals()</code> method, the <code>EditPart</code> should call <code>refresh().</code> * * In its <code>createFigure()</code> method, the <code>EditPart</code> should call the * <code>createFigure(IFigure)</code> of this class to decorate its figure. * </p> */ public class EditPartMarkerDecorator { protected EObject modelObject; // Multiple model objects that use this decorator to display markers private List<EObject> modelObjects = new ArrayList<EObject>(); private boolean fillParent = false; // A Layer to contain the Marker Images. protected Layer decorationLayer; // The layout for the decorationLayer private AbstractLayout decorationLayout; //The layout for the layer that contains the figure to be decorated. private AbstractLayout figureLayout; // by default, the children are bounded to their natural size, otherwise, the size of the children are not // touched and somebody else is responsible for sizing them protected boolean resizeChildren = true; private Object defaultConstraint = IMarkerConstants.CENTER; /** * Brand new shiny EditPartMarkerDecorator with the model object given. * * @param aModelObject the model object. */ public EditPartMarkerDecorator(EObject aModelObject) { this(aModelObject, null, null); } /** * Brand new shiny EditPartMarkerDecorator with a list of model objects. * * @param aListOfModelObjects the model object list. */ public EditPartMarkerDecorator(List<EObject> aListOfModelObjects) { this(aListOfModelObjects, null, null); } /** * Brand new shiny EditPartMarkerDecorator with a model object and a layout. * * @param aModelObject * @param aLayout the layout to use. */ public EditPartMarkerDecorator (EObject aModelObject, AbstractLayout aLayout) { this(aModelObject, aLayout, null); } /** * Brand new shiny EditPartMarkerDecorator with a list of model objects and a * layout. * * @param aListOfModelObjects * the list of model objects * @param aLayout * the layout to use. */ public EditPartMarkerDecorator(List<EObject> aListOfModelObjects, AbstractLayout aLayout) { this(aListOfModelObjects, aLayout, null); } /** * @param aModelObject * @param aFigureLayout * @param aDecorationLayout */ public EditPartMarkerDecorator(EObject aModelObject, AbstractLayout aFigureLayout, AbstractLayout aDecorationLayout) { this.modelObject = aModelObject; setFigureLayout(aFigureLayout); if (aDecorationLayout == null) { aDecorationLayout = new DecorationLayout(); } setDecorationLayout(aDecorationLayout); } /** * @param aListOfModelObjects * @param aLayout * @param aDecorationLayout */ public EditPartMarkerDecorator(List<EObject> aListOfModelObjects, AbstractLayout aLayout, AbstractLayout aDecorationLayout) { this((EObject)null, aLayout, aDecorationLayout); this.modelObjects = aListOfModelObjects; } /** * * @param fillParentSwitch */ public void setFillParent(boolean fillParentSwitch) { this.fillParent = fillParentSwitch; } static IMarker[] EMPTY_MARKERS = {}; /** * Draws the markers. * This method should be called from the EditPart's refreshVisuals() method. */ protected void refreshMarkers() { if (decorationLayer == null) { return ; } for (List<IMarker> markerList : sortByType ( getMarkers())) { IMarker[] list = markerList.toArray( EMPTY_MARKERS ); Object constraint = getConstraint(list[0]); IFigure markerFigure = createFigureForMarkers(list); if (markerFigure != null) { decorationLayer.add(markerFigure, constraint); } } } /** * Subclasses may override this. Here we sort the markers by type into several lists. * Equivalent markers are displayed as "multiple" errors. * * @param markers an array of markers. * @return a collection of lists of markers. */ protected Collection<List<IMarker>> sortByType ( IMarker[] markers ) { Map <String,List<IMarker>> sorter = new HashMap<String,List<IMarker>>(); for(IMarker m : markers) { String type = null; try { type = m.getType(); } catch (CoreException e) { continue; } List<IMarker> list = sorter.get(type); if (list == null) { list = new ArrayList<IMarker>(); sorter.put(type, list); } list.add(m); } return sorter.values(); } /** * Draws the markers. This method should be called from the EditPart's * refreshVisuals() method. */ public void refresh(){ if(decorationLayer != null) { decorationLayer.removeAll(); } refreshMarkers(); } /** * Get the image to be drawn for the marker's figure. This is obtained * from an IModelMarkerContentProvider that must be implemented by the client. * If we can't find an image using the content provider we check to see if the * marker is a problem marker and get the correct icon for it. * * May be overridden by subclasses to change the image. * * @param marker * @return an image representing the marker or null if none is available */ protected Image getImage(IMarker marker) { return ModelMarkerUtil.getImage(marker); } protected IMarker[] getMarkers() { return getMarkerMap().values().toArray(EMPTY_MARKERS); } /** * The EditPart's createFigure() method should call this method in order to decorate * it's figure. * * @param figure The figure to be decorated * @return the created figure. */ public IFigure createFigure(IFigure figure) { LayeredPane pane = new LayeredPane(); Layer layer = new Layer(); if (figureLayout == null) { if (fillParent) { figureLayout = new FillParentLayout(); } else { figureLayout = new AlignedFlowLayout() { @Override protected void setBoundsOfChild(IFigure parent, IFigure child, Rectangle bounds) { parent.getClientArea(Rectangle.SINGLETON); bounds.translate(Rectangle.SINGLETON.x, Rectangle.SINGLETON.y); if (resizeChildren) child.setBounds(bounds); else child.setLocation(bounds.getLocation()); } }; } } layer.setLayoutManager(figureLayout); pane.add(layer); layer.add(figure); if (decorationLayer == null) { decorationLayer = new Layer(); } decorationLayer.setLayoutManager(decorationLayout); pane.add(decorationLayer); return pane; } /** * Set the decoration layout. * * @param layout the layout to use */ public void setDecorationLayout(AbstractLayout layout) { decorationLayout = layout; if(decorationLayer != null) { decorationLayer.setLayoutManager(decorationLayout); } } /** * Resize children flag. * * @param resizeChildrenFlag */ public void setResizeChildren (boolean resizeChildrenFlag) { this.resizeChildren = resizeChildrenFlag; } /** * Set the figure layout the layout given. * * @param layout */ public void setFigureLayout(AbstractLayout layout) { figureLayout = layout; } /** * @return Returns the modelObject. */ public EObject getModelObject() { return modelObject; } /** * Return the list of model objects. * @return the list of model objects. */ public List<EObject> getModelObjects() { return modelObjects; } /** * Returns a map where the keys are layout constraints and the values are the * IMarkers that should be displayed for the corresponding constraint. * * May be overridden by subclasses. * * @return Map */ protected Map<Object,IMarker> getMarkerMap() { return Collections.emptyMap(); } /** * Returns the priority of the given marker * @param marker * @return the marker priority. */ protected int getPriority (IMarker marker) { Integer priority = null; // first see if we have a priority attribute try { priority = (Integer)marker.getAttribute(IModelMarkerConstants.DECORATION_MARKER_PRIORITY_ATTR); } catch (CoreException e) { // do nothing } catch (ClassCastException e) { // do nothing } if (priority != null) return priority.intValue(); // now see if is a problem marker try { if (marker.isSubtypeOf(IMarker.PROBLEM)) { int severity = marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO); if (severity == IMarker.SEVERITY_ERROR) return IMarkerConstants.PRIORITY_ERROR_INDICATOR; if (severity == IMarker.SEVERITY_WARNING) return IMarkerConstants.PRIORITY_WARNING_INDICATOR; if (severity == IMarker.SEVERITY_INFO) return IMarkerConstants.PRIORITY_INFO_INDICATOR; } } catch (CoreException e) { // do nothing } // return the default priority return IMarkerConstants.PRIORITY_DEFAULT; } /** * Default behavior. May be overridden by subclasses. * * @param marker * @return a layout constraint */ protected Object getConstraint (IMarker marker) { try { if (marker.isSubtypeOf(IModelMarkerConstants.DECORATION_GRAPHICAL_MARKER_ID)) { String key = marker.getAttribute(IModelMarkerConstants.DECORATION_GRAPHICAL_MARKER_ANCHOR_POINT_ATTR, ""); //$NON-NLS-1$ Object constraint = convertAnchorKeyToConstraint(key); if (constraint != null) { return constraint; } } } catch (CoreException e) { // Just ignore exceptions getting marker info. // It is possible that the marker no longer exists. // Eventually the UI will be notified that the // marker is removed and it will update. } return defaultConstraint; } protected Object convertAnchorKeyToConstraint(String key) { if (key.equals(IMarkerConstants.MARKER_ANCHORPOINT_TOP_CENTRE)) return IMarkerConstants.TOP; if (key.equals(IMarkerConstants.MARKER_ANCHORPOINT_BOTTOM_CENTRE)) return IMarkerConstants.BOTTOM; if (key.equals(IMarkerConstants.MARKER_ANCHORPOINT_LEFT)) return IMarkerConstants.LEFT; if (key.equals(IMarkerConstants.MARKER_ANCHORPOINT_RIGHT)) return IMarkerConstants.RIGHT; if (key.equals(IMarkerConstants.MARKER_ANCHORPOINT_CENTRE)) return IMarkerConstants.CENTER; if (key.equals(IMarkerConstants.MARKER_ANCHORPOINT_TOP_LEFT)) return IMarkerConstants.TOP_LEFT; if (key.equals(IMarkerConstants.MARKER_ANCHORPOINT_TOP_RIGHT)) return IMarkerConstants.TOP_RIGHT; if (key.equals(IMarkerConstants.MARKER_ANCHORPOINT_BOTTOM_LEFT)) return IMarkerConstants.BOTTOM_LEFT; if (key.equals(IMarkerConstants.MARKER_ANCHORPOINT_BOTTOM_RIGHT)) return IMarkerConstants.BOTTOM_RIGHT; return null; } /** * Return the default constraint. * * @return the default constraint. */ public Object getDefaultConstraint() { return defaultConstraint; } /** * Set the default constraint. * * @param aDefaultContraint the new default constraint. */ public void setDefaultConstraint(Object aDefaultContraint) { this.defaultConstraint = aDefaultContraint; } /** * Creates a figure for the given marker. * * May be overridden by subclasses to change the figure created. * @param marker * @return the figure for the marker */ protected IFigure createFigureForMarker(IMarker marker) { Image image = getImage(marker); String text = getText(marker); if (image != null) { ImageFigure imageFigure = new ImageFigure(image); if (text != null) { imageFigure.setToolTip(new Label(text)); } return imageFigure; } return null; } /** * Creates a figure for the given markers. * * May be overridden by subclasses to change the figure created. * @param marker * @return the figure for the marker */ @SuppressWarnings({ "boxing", "nls" }) protected IFigure createFigureForMarkers ( IMarker[] markers ) { if (markers.length == 1) { return createFigureForMarker(markers[0]); } Image image = null; StringBuilder builder = new StringBuilder(128); builder.append( NLS.bind(Messages.EditPartMarkerEectorator_1, (new Object[] { markers.length })) ); for(IMarker m : markers) { if (image == null) { image = getImage(m); } String text = getText(m); if (text != null) { builder.append("\no ").append(text); } } if (image != null) { ImageFigure imageFigure = new ImageFigure(image); imageFigure.setToolTip(new Label( builder.toString() )); return imageFigure; } return null; } /** * Get the tooltip text for the marker's figure. This is obtained * from an IModelMarkerContentProvider that must be implemented by the client. * If we can't get the text using a content provider we check to see if the * marker is a problem marker and get the correct text for it. * * May be overridden by subclasses to change the tooltip text. * * @param marker for which to retrieve the tooltip text * @return a String of text to display as a tooltip for the marker */ protected String getText(IMarker marker) { return ModelMarkerUtil.getText(marker); } /** * Determines whether the marker is acceptable and should be shown * for this edit part. * * @param marker the marker * @return <code>true</code> if the marker is acceptable */ protected boolean isAcceptable(IMarker marker) { return true; } }