| /****************************************************************************** |
| * Copyright (c) 2002, 2010 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.gmf.runtime.gef.ui.figures; |
| |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| |
| import org.eclipse.draw2d.ConnectionAnchor; |
| import org.eclipse.draw2d.Figure; |
| import org.eclipse.draw2d.Graphics; |
| import org.eclipse.draw2d.geometry.Insets; |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.PointList; |
| import org.eclipse.draw2d.geometry.PrecisionPoint; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| import org.eclipse.gef.handles.HandleBounds; |
| import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor; |
| import org.eclipse.gmf.runtime.draw2d.ui.figures.IAnchorableFigure; |
| import org.eclipse.gmf.runtime.draw2d.ui.figures.IPolygonAnchorableFigure; |
| import org.eclipse.gmf.runtime.draw2d.ui.graphics.ColorRegistry; |
| import org.eclipse.gmf.runtime.draw2d.ui.internal.figures.TransparentBorder; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Path; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.PlatformUI; |
| |
| /** |
| * Base class that most shape figures should extend from to gain default connection anchor behavior. |
| * |
| * @author melaasar |
| */ |
| public class NodeFigure |
| extends Figure |
| implements HandleBounds, IAnchorableFigure, IPolygonAnchorableFigure { |
| |
| private Hashtable connectionAnchors; |
| |
| /** |
| * The width of this shape's outline. (a field from GEF Shape). Must be |
| * greater than zero. |
| * TODO: NodeFigure should have extended org.eclipse.draw2d.Shape |
| */ |
| private int lineWidth = 1; |
| |
| /** |
| * The line style to be used for this shape's outline. |
| * TODO: NodeFigure should have extended org.eclipse.draw2d.Shape |
| */ |
| private int lineStyle = Graphics.LINE_SOLID; |
| |
| private boolean isUsingGradient = false; |
| |
| private int gradientColor1 = -1; |
| |
| private int gradientColor2 = -1; |
| |
| private int gradientStyle = 0; |
| |
| /** |
| * The transparency of this shape in percent. |
| * Must be in [0, 100] range. |
| */ |
| private int transparency = 0; |
| |
| /** |
| * <code>String</code> that is the identifier for the default anchor |
| */ |
| static public final String szAnchor = ""; //$NON-NLS-1$ |
| |
| /** |
| * Constructor - sets the default colors for all node figures. |
| */ |
| public NodeFigure() { |
| // empty constructor |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.gef.handles.HandleBounds#getHandleBounds() |
| */ |
| public Rectangle getHandleBounds() { |
| Insets insets = new Insets(0, 0, 0, 0); |
| if (getBorder() instanceof TransparentBorder) { |
| insets = |
| ((TransparentBorder) getBorder()).getTransparentInsets(this); |
| } |
| |
| // Ignore the insets when placing the handles |
| return new Rectangle( |
| getBounds().x + insets.left, |
| getBounds().y + insets.top, |
| getBounds().width - (insets.right + insets.left), |
| getBounds().height - (insets.bottom + insets.top)); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.draw2d.ui.figures.IAnchorableFigure#getConnectionAnchor(java.lang.String) |
| */ |
| public ConnectionAnchor getConnectionAnchor(String terminal) { |
| |
| ConnectionAnchor connectAnchor = |
| (ConnectionAnchor) getConnectionAnchors().get(terminal); |
| if (connectAnchor == null) { |
| if (terminal.equals(szAnchor)) { |
| // get a new one - this figure doesn't support static anchors |
| connectAnchor = createDefaultAnchor(); |
| getConnectionAnchors().put(terminal,connectAnchor); |
| } |
| else { |
| connectAnchor = createAnchor(SlidableAnchor.parseTerminalString(terminal)); |
| } |
| } |
| |
| return connectAnchor; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.draw2d.ui.figures.IAnchorableFigure#getConnectionAnchorTerminal(org.eclipse.draw2d.ConnectionAnchor) |
| */ |
| public String getConnectionAnchorTerminal(ConnectionAnchor c) { |
| if (c instanceof SlidableAnchor) { |
| return ((SlidableAnchor) c).getTerminal(); |
| } |
| if (getConnectionAnchors().containsValue(c)) { |
| Iterator iter = getConnectionAnchors().keySet().iterator(); |
| String key; |
| while (iter.hasNext()) { |
| key = (String) iter.next(); |
| if (getConnectionAnchors().get(key).equals(c)) |
| return key; |
| } |
| } |
| getConnectionAnchor(szAnchor); |
| return szAnchor; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.draw2d.ui.figures.IAnchorableFigure#getSourceConnectionAnchorAt(org.eclipse.draw2d.geometry.Point) |
| */ |
| public ConnectionAnchor getSourceConnectionAnchorAt(Point p) { |
| return createConnectionAnchor(p); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.draw2d.ui.figures.IAnchorableFigure#getTargetConnectionAnchorAt(org.eclipse.draw2d.geometry.Point) |
| */ |
| public ConnectionAnchor getTargetConnectionAnchorAt(Point p) { |
| return createConnectionAnchor(p); |
| } |
| |
| /** |
| * Returns a new anchor for this node figure. |
| * |
| * @param p <code>Point</code> on the figure that gives a hint which anchor to return. |
| * @return <code>ConnectionAnchor</code> reference to an anchor associated with the |
| * given point on the figure. |
| */ |
| protected ConnectionAnchor createConnectionAnchor(Point p) { |
| if (p == null) { |
| return getConnectionAnchor(szAnchor); |
| } |
| else { |
| Point temp = p.getCopy(); |
| translateToRelative(temp); |
| PrecisionPoint pt = BaseSlidableAnchor.getAnchorRelativeLocation(temp, getBounds()); |
| if (isDefaultAnchorArea(pt)) |
| return getConnectionAnchor(szAnchor); |
| return createAnchor(pt); |
| } |
| } |
| |
| /** |
| * Checks whether the <PrecisionPoint> p which is a candidate for a relative reference |
| * for the <Code>SlidableAnchor</Code> belongs to the area where the default anchor |
| * must be created |
| * |
| * @param p |
| * @return <code>boolean</code> <code>true</code> if <PrecisionPoint> belongs to the area where the default anchor must be |
| * created, <code>false</code> otherwise |
| */ |
| protected boolean isDefaultAnchorArea(PrecisionPoint p) { |
| return p.preciseX >= getSlidableAnchorArea()/2 && p.preciseX <= 1 - getSlidableAnchorArea()/2 && |
| p.preciseY >= getSlidableAnchorArea()/2 && p.preciseY <= 1 - getSlidableAnchorArea()/2; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.draw2d.Figure#paintFigure(org.eclipse.draw2d.Graphics) |
| */ |
| protected void paintFigure(Graphics graphics) { |
| if (isOpaque() && getBorder() != null) { |
| Rectangle tempRect = new Rectangle(getBounds()); |
| tempRect.crop(getBorder().getInsets(this)); |
| graphics.fillRectangle(tempRect); |
| return; |
| } |
| |
| super.paintFigure(graphics); |
| } |
| |
| |
| /** |
| * Returns the connectionAnchors. |
| * @return Hashtable |
| */ |
| protected Hashtable getConnectionAnchors() { |
| if (connectionAnchors == null) |
| connectionAnchors = new Hashtable(1); |
| return connectionAnchors; |
| } |
| |
| |
| /** |
| * Specifies how large the area of the figure's bounds where <Code>SlidableAnchor</Code> |
| * will be created. The result number: 0<=result<=1 |
| * |
| * @return the size of the area of the figure's bounds |
| */ |
| protected double getSlidableAnchorArea() { |
| return 0.5; |
| } |
| |
| /** |
| * Creates the default Slidable anchor with a reference point at the center |
| * of the figure's bounds |
| * |
| * @return - default SlidableAnchor, relative reference the center of the figure |
| */ |
| protected ConnectionAnchor createDefaultAnchor() { |
| return new SlidableAnchor(this); |
| } |
| |
| /** |
| * Creates a slidable anchor at the specified point (from the ratio of the |
| * reference's coordinates and bounds of the figure |
| * |
| * @param p - relative reference for the <Code>SlidableAnchor</Code> |
| * @return a <code>SlidableAnchor</code> for this figure with relative reference at p |
| */ |
| protected ConnectionAnchor createAnchor(PrecisionPoint p) { |
| if (p==null) |
| // If the old terminal for the connection anchor cannot be resolved (by SlidableAnchor) a null |
| // PrecisionPoint will passed in - this is handled here |
| return createDefaultAnchor(); |
| return new SlidableAnchor(this, p); |
| } |
| |
| /** |
| * @return <code>String</code> that is the identifier for the default anchor |
| */ |
| public static String getDefaultAnchorID() { |
| return szAnchor; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.draw2d.ui.figures.IPolygonAnchorableFigure#getPolygonPoints() |
| */ |
| public PointList getPolygonPoints() { |
| PointList points = new PointList(5); |
| Rectangle anchorableRectangle = getHandleBounds(); |
| points.addPoint(anchorableRectangle.x, anchorableRectangle.y); |
| points.addPoint(anchorableRectangle.x + anchorableRectangle.width, |
| anchorableRectangle.y); |
| points.addPoint(anchorableRectangle.x + anchorableRectangle.width, |
| anchorableRectangle.y + anchorableRectangle.height); |
| points.addPoint(anchorableRectangle.x, anchorableRectangle.y |
| + anchorableRectangle.height); |
| points.addPoint(anchorableRectangle.x, anchorableRectangle.y); |
| return points; |
| } |
| |
| /** |
| * Returns the line style used to outline this shape. |
| * @return the line style |
| * @since 2.1 |
| */ |
| public int getLineStyle() { |
| return lineStyle; |
| } |
| |
| /** |
| * Returns the line width of this shape's outline. |
| * @return the line width |
| * @since 2.1 |
| */ |
| public int getLineWidth() { |
| return lineWidth; |
| } |
| |
| /** |
| * Sets the line width to be used to outline the shape. |
| * |
| * @param w the new width |
| * @since 2.1 |
| */ |
| public void setLineWidth(int w) { |
| if ((lineWidth == w) || (w < 0)) |
| return; |
| lineWidth = w; |
| repaint(); |
| } |
| |
| /** |
| * Sets the style of line to be used by this shape. |
| * |
| * @param s the new line style |
| * @since 2.1 |
| */ |
| public void setLineStyle(int s) { |
| if (lineStyle == s) |
| return; |
| lineStyle = s; |
| repaint(); |
| } |
| |
| /** |
| * @return the gradientColor1 |
| * @since 1.2 |
| */ |
| public int getGradientColor1() { |
| return gradientColor1; |
| } |
| |
| /** |
| * @return the gradientColor2 |
| * @since 1.2 |
| */ |
| public int getGradientColor2() { |
| return gradientColor2; |
| } |
| |
| /** |
| * @return the gradientStyle |
| * @since 1.2 |
| */ |
| public int getGradientStyle() { |
| return gradientStyle; |
| } |
| |
| /** |
| * Sets values defining gradient data. |
| * |
| * @param gradientColor1 |
| * @param gradientColor2 |
| * @param gradientStyle |
| * @since 1.2 |
| */ |
| public void setGradientData(int gradientColor1, int gradientColor2, int gradientStyle) { |
| boolean doRepaint = false; |
| // When in high contrast mode, there is no gradient, so set bg color to null to allow inheriting |
| // the system color. |
| Display display = Display.getCurrent(); |
| if (display == null && PlatformUI.isWorkbenchRunning()) { |
| display = PlatformUI.getWorkbench().getDisplay(); |
| } |
| if (display != null && display.getHighContrast()) { |
| setBackgroundColor(null); |
| return; |
| } |
| if (gradientColor1 != this.gradientColor1 && gradientColor1 > -1) { |
| this.gradientColor1 = gradientColor1; |
| doRepaint = true; |
| } |
| if (gradientColor2 != this.gradientColor2 && gradientColor2 > -1) { |
| this.gradientColor2 = gradientColor2; |
| doRepaint = true; |
| } |
| if (gradientStyle != this.gradientStyle) { |
| this.gradientStyle = gradientStyle; |
| doRepaint = true; |
| } |
| if (doRepaint) { |
| repaint(); |
| } |
| } |
| |
| /** |
| * Utility method that indicates if gradient should be used as a fill style or not. |
| * |
| * @return true if gradient should be used, false otherwise (fill color should be used) |
| * @since 1.2 |
| */ |
| public boolean isUsingGradient() { |
| // When in high contrast mode, there is no gradient, so return false. |
| Display display = Display.getCurrent(); |
| if (display == null && PlatformUI.isWorkbenchRunning()) { |
| display = PlatformUI.getWorkbench().getDisplay(); |
| } |
| if (display != null && display.getHighContrast()) { |
| return false; |
| } |
| return isUsingGradient && gradientColor1 > -1 && gradientColor2 > -1; |
| } |
| |
| /** |
| * Sets the value of isUsingGradient |
| * |
| * @param b value for isUsingGradient |
| * @since 1.2 |
| */ |
| public void setIsUsingGradient(boolean b) { |
| if (b != isUsingGradient) { |
| isUsingGradient = b; |
| // this is needed, e.g. when undoing gradient clearing from Advanced tab; |
| // in cases when repaint() is already called, it doesn't matter since |
| // the actual paint happens only once |
| repaint(); |
| } |
| } |
| |
| /** |
| * Fills given path by gradient using given fillMode |
| * |
| * @param g The Graphics used to paint |
| * @param path Path of shape to be filled with gradient |
| * @param fillMode One of SWT.FILL_EVEN_ODD and SWT.FILL_WINDING |
| * @since 1.2 |
| */ |
| protected void fillGradient(Graphics g, Path path, int fillMode) { |
| if (path != null) { |
| g.pushState(); |
| g.setForegroundColor(ColorRegistry.getInstance().getColor(Integer.valueOf(getGradientColor1()))); |
| g.setBackgroundColor(ColorRegistry.getInstance().getColor(Integer.valueOf(getGradientColor2()))); |
| if (fillMode == SWT.FILL_EVEN_ODD || fillMode == SWT.FILL_WINDING) { |
| g.setFillRule(fillMode); |
| } |
| g.clipPath(path); |
| g.fillGradient(getBounds(), getGradientStyle() == 0); |
| path.dispose(); |
| g.popState(); |
| } |
| } |
| |
| /** |
| * Fills gradient using default mode SWT.FILL_EVEN_ODD and getPath() to |
| * obtain path to fill. |
| * |
| * @param g The Graphics used to paint |
| * @since 1.2 |
| */ |
| protected void fillGradient(Graphics g) { |
| // use the default mode if one is not provided |
| fillGradient(g, getPath(), SWT.FILL_EVEN_ODD); |
| } |
| |
| /** |
| * Fills gradient using default mode SWT.FILL_EVEN_ODD. Use this method when |
| * getPath() doesn't return desired path. |
| * |
| * @param g The Graphics used to paint |
| * @param path Path of shape to be filled with gradient |
| * @since 1.2 |
| */ |
| protected void fillGradient(Graphics g, Path path) { |
| // use the default mode if one is not provided |
| fillGradient(g, path, SWT.FILL_EVEN_ODD); |
| } |
| |
| /** |
| * Fills gradient using getPath() to obtain path to fill. Use this method |
| * when default fill mode SWT.FILL_EVEN_ODD is not appropriate. |
| * |
| * @param g The Graphics used to paint |
| * @param fillMode One of SWT.FILL_EVEN_ODD and SWT.FILL_WINDING |
| * @since 1.2 |
| */ |
| protected void fillGradient(Graphics g, int fillMode) { |
| fillGradient(g, getPath(), fillMode); |
| } |
| |
| /** |
| * This method creates and returns figure's path. Default implementation defines path |
| * based on figure's bounds and insets. Subclasses should override if |
| * needed. |
| * |
| * @return Created path |
| * @since 1.2 |
| */ |
| protected Path getPath() { |
| if (!isOpaque()) { |
| return null; |
| } |
| Path path = new Path(null); |
| Rectangle tempRect = getClientArea(); |
| path.addRectangle(tempRect.x, tempRect.y, tempRect.width, tempRect.height); |
| return path; |
| } |
| |
| |
| /** |
| * Returns transparency value (belongs to [0, 100] interval) |
| * |
| * @return transparency |
| * @since 1.2 |
| */ |
| public int getTransparency() { |
| return transparency; |
| } |
| |
| /** |
| * Sets the transparency if the given parameter is in [0, 100] range |
| * |
| * @param transparency The transparency to set |
| * @since 1.2 |
| */ |
| public void setTransparency(int transparency) { |
| if (transparency != this.transparency && |
| transparency >= 0 && transparency <= 100) { |
| this.transparency = transparency; |
| repaint(); |
| } |
| } |
| |
| /** |
| * Converts transparency value from percent range [0, 100] to alpha range |
| * [0, 255] and applies converted value. 0% corresponds to alpha 255 and |
| * 100% corresponds to alpha 0. |
| * |
| * @param g The Graphics used to paint |
| * @since 1.2 |
| */ |
| protected void applyTransparency(Graphics g) { |
| g.setAlpha(255 - transparency * 255 / 100); |
| } |
| |
| } |