/******************************************************************************* * Copyright (c) 2000, 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.draw2d; import java.util.ArrayList; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.draw2d.rap.swt.graphics.ColorUtil; import org.eclipse.draw2d.rap.swt.graphics.FontUtil; import org.eclipse.rap.rwt.SingletonUtil; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.widgets.Shell; /** * Provides miscellaneous Figure operations. */ public class FigureUtilities { private static final float RGB_VALUE_MULTIPLIER = 0.6f; // [RAP AM] remove static usage private GC gc; private Font appliedFont; private FontMetrics metrics; // private static Color ghostFillColor = new Color(null, 31, 31, 31); private static Color ghostFillColor() { return ColorUtil.getColor( 31, 31, 31 ); } private static FigureUtilities instance() { return SingletonUtil.getSessionInstance( FigureUtilities.class ); } /** * Returns a new Color the same as the passed color in a darker hue. * * @param color the color to darken * @return the darkened color * @since 2.0 */ public static Color darker( Color color ) { return new Color( null, ( int )( color.getRed() * RGB_VALUE_MULTIPLIER ), ( int )( color.getGreen() * RGB_VALUE_MULTIPLIER ), ( int )( color.getBlue() * RGB_VALUE_MULTIPLIER ) ); } /** * Returns the FontMetrics associated with the passed Font. * * @param f the font * @return the FontMetrics for the given font * @see GC#getFontMetrics() * @since 2.0 */ public static FontMetrics getFontMetrics( Font f ) { setFont( f ); FigureUtilities fu = instance(); if( fu.metrics == null ) fu.metrics = getGC().getFontMetrics(); return fu.metrics; } /** * Returns the GC used for various utilities. Advanced graphics must not be switched on by clients * using this GC. * * @deprecated do not mess with this GC * @return the GC */ protected static GC getGC() { FigureUtilities fu = instance(); if( fu.gc == null ) { fu.gc = new GC( new Shell() ); fu.appliedFont = fu.gc.getFont(); } return fu.gc; } /** * Returns the dimensions of the String <i>s</i> using the font <i>f</i>. Tab expansion and * carriage return processing are performed. * * @param s the string * @param f the font * @return the text's dimensions * @see GC#textExtent(String) */ protected static org.eclipse.swt.graphics.Point getTextDimension( String s, Font f ) { setFont( f ); return getGC().textExtent( s ); } /** * Returns the highest ancestor for the given figure * * @since 3.0 * @param figure a figure * @return the root ancestor */ public static IFigure getRoot( IFigure figure ) { while( figure.getParent() != null ) figure = figure.getParent(); return figure; } /** * Returns the dimensions of the String <i>s</i> using the font <i>f</i>. No tab expansion or * carriage return processing will be performed. * * @param s the string * @param f the font * @return the string's dimensions * @see GC#stringExtent(java.lang.String) */ protected static org.eclipse.swt.graphics.Point getStringDimension( String s, Font f ) { setFont( f ); return getGC().stringExtent( s ); } /** * Returns the Dimensions of the given text, converting newlines and tabs appropriately. * * @param text the text * @param f the font * @return the dimensions of the given text * @since 2.0 */ public static Dimension getTextExtents( String text, Font f ) { return new Dimension( getTextDimension( text, f ) ); } /** * Returns the Dimensions of <i>s</i> in Font <i>f</i>. * * @param s the string * @param f the font * @return the dimensions of the given string * @since 2.0 */ public static Dimension getStringExtents( String s, Font f ) { return new Dimension( getStringDimension( s, f ) ); } /** * Returns the Dimensions of the given text, converting newlines and tabs appropriately. * * @param s the string * @param f the font * @param result the Dimension that will contain the result of this calculation * @since 2.0 */ public static void getTextExtents( String s, Font f, Dimension result ) { org.eclipse.swt.graphics.Point pt = getTextDimension( s, f ); result.width = pt.x; result.height = pt.y; } /** * Returns the width of <i>s</i> in Font <i>f</i>. * * @param s the string * @param f the font * @return the width * @since 2.0 */ public static int getTextWidth( String s, Font f ) { return getTextDimension( s, f ).x; } /** * Returns a Color the same as the passed color in a lighter hue. * * @param rgb the color * @return the lighter color * @since 2.0 */ public static Color lighter( Color rgb ) { int r = rgb.getRed(), g = rgb.getGreen(), b = rgb.getBlue(); return new Color( null, Math.max( 2, Math.min( ( int )( r / RGB_VALUE_MULTIPLIER ), 255 ) ), Math.max( 2, Math.min( ( int )( g / RGB_VALUE_MULTIPLIER ), 255 ) ), Math.max( 2, Math.min( ( int )( b / RGB_VALUE_MULTIPLIER ), 255 ) ) ); } /** * Produces a ghosting effect on the shape <i>s</i>. * * @param s the shape * @return the ghosted shape * @since 2.0 */ public static Shape makeGhostShape( Shape s ) { s.setBackgroundColor( ghostFillColor() ); s.setFillXOR( true ); s.setOutlineXOR( true ); return s; } /** * Mixes the passed Colors and returns the resulting Color. * * @param c1 the first color * @param c2 the second color * @param weight the first color's weight from 0-1 * @return the new color * @since 2.0 */ public static Color mixColors( Color c1, Color c2, double weight ) { return new Color( null, ( int )( c1.getRed() * weight + c2.getRed() * ( 1 - weight ) ), ( int )( c1.getGreen() * weight + c2.getGreen() * ( 1 - weight ) ), ( int )( c1.getBlue() * weight + c2.getBlue() * ( 1 - weight ) ) ); } /** * Mixes the passed Colors and returns the resulting Color. * * @param c1 the first color * @param c2 the second color * @return the new color * @since 2.0 */ public static Color mixColors( Color c1, Color c2 ) { return new Color( null, ( c1.getRed() + c2.getRed() ) / 2, ( c1.getGreen() + c2.getGreen() ) / 2, ( c1.getBlue() + c2.getBlue() ) / 2 ); } /** * Paints a border with an etching effect, having a shadow of Color <i>shadow</i> and highlight of * Color <i>highlight</i>. * * @param g the graphics object * @param r the bounds of the border * @param shadow the shadow color * @param highlight the highlight color * @since 2.0 */ public static void paintEtchedBorder( Graphics g, Rectangle r, Color shadow, Color highlight ) { int x = r.x, y = r.y, w = r.width, h = r.height; g.setLineStyle( Graphics.LINE_SOLID ); g.setLineWidth( 1 ); g.setXORMode( false ); w -= 2; h -= 2; g.setForegroundColor( shadow ); g.drawRectangle( x, y, w, h ); x++ ; y++ ; g.setForegroundColor( highlight ); g.drawRectangle( x, y, w, h ); } /** * Helper method to paint a grid. Painting is optimized as it is restricted to the Graphics' clip. * * @param g The Graphics object to be used for painting * @param f The figure in which the grid is to be painted * @param origin Any point where the grid lines are expected to intersect * @param distanceX Distance between vertical grid lines; if 0 or less, vertical grid lines will * not be drawn * @param distanceY Distance between horizontal grid lines; if 0 or less, horizontal grid lines * will not be drawn * @since 3.0 */ public static void paintGrid( Graphics g, IFigure f, org.eclipse.draw2d.geometry.Point origin, int distanceX, int distanceY ) { Rectangle clip = g.getClip( Rectangle.getSINGLETON() ); if( distanceX > 0 ) { if( origin.x >= clip.x ) while( origin.x - distanceX >= clip.x ) origin.x -= distanceX; else while( origin.x < clip.x ) origin.x += distanceX; for( int i = origin.x; i < clip.x + clip.width; i += distanceX ) g.drawLine( i, clip.y, i, clip.y + clip.height ); } if( distanceY > 0 ) { if( origin.y >= clip.y ) while( origin.y - distanceY >= clip.y ) origin.y -= distanceY; else while( origin.y < clip.y ) origin.y += distanceY; for( int i = origin.y; i < clip.y + clip.height; i += distanceY ) g.drawLine( clip.x, i, clip.x + clip.width, i ); } } /** * Paints a border with an etching effect, having a shadow of a darker version of g's background * color, and a highlight a lighter version of g's background color. * * @param g the graphics object * @param r the bounds of the border * @since 2.0 */ public static void paintEtchedBorder( Graphics g, Rectangle r ) { Color rgb = g.getBackgroundColor(), shadow = darker( rgb ), highlight = lighter( rgb ); paintEtchedBorder( g, r, shadow, highlight ); } /** * Sets Font to passed value. * * @param f the new font * @since 2.0 */ protected static void setFont( Font f ) { FigureUtilities fu = instance(); f = FontUtil.getFont( f.getFontData() ); getGC().setFont( f ); fu.appliedFont = f; fu.metrics = null; } /** * Returns the figure which is the nearest common ancestor of both figures, or <code>null</code> * if there is no common ancestor. A figure is an ancestor if it is the parent of another figure, * or if it is the ancestor of that figure's parent. If one figure is the ancestor of the other, * it is returned as the common ancestor. * * @since 3.1 * @param l left * @param r right * @return the common ancestor, if it exists, or <code>null</code>. */ public static IFigure findCommonAncestor( IFigure l, IFigure r ) { if( l == r ) return l; ArrayList left = new ArrayList(); ArrayList right = new ArrayList(); while( l != null ) { left.add( l ); l = l.getParent(); } while( r != null ) { right.add( r ); r = r.getParent(); } if( left.isEmpty() || right.isEmpty() ) return null; for( int i = 0; i < left.size(); i++ ) { if( right.contains( left.get( i ) ) ) return ( IFigure )left.get( i ); } return null; } /** * Returns <code>true</code> if the ancestor contains the descendant, or is the ancestor of the * descendant's parent. * * @param ancestor the ancestor * @param descendant the descendant * @return <code>true</code> if ancestor * @since 3.2 */ public static boolean isAncestor( final IFigure ancestor, IFigure descendant ) { while( descendant != null ) { descendant = descendant.getParent(); if( descendant == ancestor ) return true; } return false; } /** * Determines whether the given figure is showing and not (completely) clipped. * * @param figure The figure to test * @return <code>true</code> if the given figure is showing and not completely clipped, * <code>false</code> otherwise. * @since 3.7 */ public static boolean isNotFullyClipped( IFigure figure ) { if( figure == null || !figure.isShowing() ) { return false; } // check if figure is clipped // TODO: IClippingStrategy has to be taken into consideration as well. Rectangle figBounds = figure.getBounds().getCopy(); IFigure walker = figure.getParent(); while( !figBounds.isEmpty() && walker != null ) { walker.translateToParent( figBounds ); figBounds.intersect( walker.getBounds() ); walker = walker.getParent(); } return !figBounds.isEmpty(); } }