mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			822 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			822 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Java
		
	
	
	
/* BasicGraphicsUtils.java
 | 
						|
   Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
package javax.swing.plaf.basic;
 | 
						|
 | 
						|
import gnu.classpath.SystemProperties;
 | 
						|
 | 
						|
import java.awt.Color;
 | 
						|
import java.awt.Dimension;
 | 
						|
import java.awt.Font;
 | 
						|
import java.awt.FontMetrics;
 | 
						|
import java.awt.Graphics;
 | 
						|
import java.awt.Graphics2D;
 | 
						|
import java.awt.Insets;
 | 
						|
import java.awt.Rectangle;
 | 
						|
import java.awt.font.FontRenderContext;
 | 
						|
import java.awt.font.LineMetrics;
 | 
						|
import java.awt.font.TextLayout;
 | 
						|
import java.awt.geom.Rectangle2D;
 | 
						|
 | 
						|
import javax.swing.AbstractButton;
 | 
						|
import javax.swing.Icon;
 | 
						|
import javax.swing.JComponent;
 | 
						|
import javax.swing.SwingUtilities;
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * A utility class providing commonly used drawing and measurement
 | 
						|
 * routines.
 | 
						|
 *
 | 
						|
 * @author Sascha Brawer (brawer@dandelis.ch)
 | 
						|
 */
 | 
						|
public class BasicGraphicsUtils
 | 
						|
{
 | 
						|
  /**
 | 
						|
   * Used as a key for a client property to store cached TextLayouts in. This
 | 
						|
   * is used for speed-up drawing of text in
 | 
						|
   * {@link #drawString(Graphics, String, int, int, int)}.
 | 
						|
   */
 | 
						|
  static final String CACHED_TEXT_LAYOUT =
 | 
						|
    "BasicGraphicsUtils.cachedTextLayout";
 | 
						|
 | 
						|
  /**
 | 
						|
   * Constructor. It is utterly unclear why this class should
 | 
						|
   * be constructable, but this is what the API specification
 | 
						|
   * says.
 | 
						|
   */
 | 
						|
  public BasicGraphicsUtils()
 | 
						|
  {
 | 
						|
    // Nothing to do here.
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a rectangle that appears etched into the surface, given
 | 
						|
   * four colors that are used for drawing.
 | 
						|
   *
 | 
						|
   * <p><img src="doc-files/BasicGraphicsUtils-1.png" width="360"
 | 
						|
   * height="200" alt="[An illustration that shows which pixels
 | 
						|
   * get painted in what color]" />
 | 
						|
   *
 | 
						|
   * @param g the graphics into which the rectangle is drawn.
 | 
						|
   * @param x the x coordinate of the rectangle.
 | 
						|
   * @param y the y coordinate of the rectangle.
 | 
						|
   * @param width the width of the rectangle in pixels.
 | 
						|
   * @param height the height of the rectangle in pixels.
 | 
						|
   *
 | 
						|
   * @param shadow the color that will be used for painting
 | 
						|
   *        the outer side of the top and left edges.
 | 
						|
   *
 | 
						|
   * @param darkShadow the color that will be used for painting
 | 
						|
   *        the inner side of the top and left edges.
 | 
						|
   *
 | 
						|
   * @param highlight the color that will be used for painting
 | 
						|
   *        the inner side of the bottom and right edges.
 | 
						|
   *
 | 
						|
   * @param lightHighlight the color that will be used for painting
 | 
						|
   *        the outer side of the bottom and right edges.
 | 
						|
   *
 | 
						|
   * @see #getEtchedInsets()
 | 
						|
   * @see javax.swing.border.EtchedBorder
 | 
						|
   */
 | 
						|
  public static void drawEtchedRect(Graphics g,
 | 
						|
                                    int x, int y, int width, int height,
 | 
						|
                                    Color shadow, Color darkShadow,
 | 
						|
                                    Color highlight, Color lightHighlight)
 | 
						|
  {
 | 
						|
    Color oldColor;
 | 
						|
    int x2, y2;
 | 
						|
 | 
						|
    oldColor = g.getColor();
 | 
						|
    x2 = x + width - 1;
 | 
						|
    y2 = y + height - 1;
 | 
						|
 | 
						|
    try
 | 
						|
    {
 | 
						|
      /* To understand this code, it might be helpful to look at the
 | 
						|
       * image "BasicGraphicsUtils-1.png" that is included with the
 | 
						|
       * JavaDoc. The file is located in the "doc-files" subdirectory.
 | 
						|
       *
 | 
						|
       * (x2, y2) is the coordinate of the most right and bottom pixel
 | 
						|
       * to be painted.
 | 
						|
       */
 | 
						|
      g.setColor(shadow);
 | 
						|
      g.drawLine(x, y, x2 - 1, y);                     // top, outer
 | 
						|
      g.drawLine(x, y + 1, x, y2 - 1);                 // left, outer
 | 
						|
 | 
						|
      g.setColor(darkShadow);
 | 
						|
      g.drawLine(x + 1, y + 1, x2 - 2, y + 1);         // top, inner
 | 
						|
      g.drawLine(x + 1, y + 2, x + 1, y2 - 2);         // left, inner
 | 
						|
 | 
						|
      g.setColor(highlight);
 | 
						|
      g.drawLine(x + 1, y2 - 1, x2 - 1, y2 - 1);       // bottom, inner
 | 
						|
      g.drawLine(x2 - 1, y + 1, x2 - 1, y2 - 2);       // right, inner
 | 
						|
 | 
						|
      g.setColor(lightHighlight);
 | 
						|
      g.drawLine(x, y2, x2, y2);                       // bottom, outer
 | 
						|
      g.drawLine(x2, y, x2, y2 - 1);                   // right, outer
 | 
						|
    }
 | 
						|
    finally
 | 
						|
    {
 | 
						|
      g.setColor(oldColor);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Determines the width of the border that gets painted by
 | 
						|
   * {@link #drawEtchedRect}.
 | 
						|
   *
 | 
						|
   * @return an <code>Insets</code> object whose <code>top</code>,
 | 
						|
   *         <code>left</code>, <code>bottom</code> and
 | 
						|
   *         <code>right</code> field contain the border width at the
 | 
						|
   *         respective edge in pixels.
 | 
						|
   */
 | 
						|
  public static Insets getEtchedInsets()
 | 
						|
  {
 | 
						|
    return new Insets(2, 2, 2, 2);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a rectangle that appears etched into the surface, given
 | 
						|
   * two colors that are used for drawing.
 | 
						|
   *
 | 
						|
   * <p><img src="doc-files/BasicGraphicsUtils-2.png" width="360"
 | 
						|
   * height="200" alt="[An illustration that shows which pixels
 | 
						|
   * get painted in what color]" />
 | 
						|
   *
 | 
						|
   * @param g the graphics into which the rectangle is drawn.
 | 
						|
   * @param x the x coordinate of the rectangle.
 | 
						|
   * @param y the y coordinate of the rectangle.
 | 
						|
   * @param width the width of the rectangle in pixels.
 | 
						|
   * @param height the height of the rectangle in pixels.
 | 
						|
   *
 | 
						|
   * @param shadow the color that will be used for painting the outer
 | 
						|
   *        side of the top and left edges, and for the inner side of
 | 
						|
   *        the bottom and right ones.
 | 
						|
   *
 | 
						|
   * @param highlight the color that will be used for painting the
 | 
						|
   *        inner side of the top and left edges, and for the outer
 | 
						|
   *        side of the bottom and right ones.
 | 
						|
   *
 | 
						|
   * @see #getGrooveInsets()
 | 
						|
   * @see javax.swing.border.EtchedBorder
 | 
						|
   */
 | 
						|
  public static void drawGroove(Graphics g,
 | 
						|
                                int x, int y, int width, int height,
 | 
						|
                                Color shadow, Color highlight)
 | 
						|
  {
 | 
						|
    /* To understand this, it might be helpful to look at the image
 | 
						|
     * "BasicGraphicsUtils-2.png" that is included with the JavaDoc,
 | 
						|
     * and to compare it with "BasicGraphicsUtils-1.png" which shows
 | 
						|
     * the pixels painted by drawEtchedRect.  These image files are
 | 
						|
     * located in the "doc-files" subdirectory.
 | 
						|
     */
 | 
						|
    drawEtchedRect(g, x, y, width, height,
 | 
						|
                   /* outer topLeft */     shadow,
 | 
						|
                   /* inner topLeft */     highlight,
 | 
						|
                   /* inner bottomRight */ shadow,
 | 
						|
                   /* outer bottomRight */ highlight);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Determines the width of the border that gets painted by
 | 
						|
   * {@link #drawGroove}.
 | 
						|
   *
 | 
						|
   * @return an <code>Insets</code> object whose <code>top</code>,
 | 
						|
   *         <code>left</code>, <code>bottom</code> and
 | 
						|
   *         <code>right</code> field contain the border width at the
 | 
						|
   *         respective edge in pixels.
 | 
						|
   */
 | 
						|
  public static Insets getGrooveInsets()
 | 
						|
  {
 | 
						|
    return new Insets(2, 2, 2, 2);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a border that is suitable for buttons of the Basic look and
 | 
						|
   * feel.
 | 
						|
   *
 | 
						|
   * <p><img src="doc-files/BasicGraphicsUtils-3.png" width="500"
 | 
						|
   * height="300" alt="[An illustration that shows which pixels
 | 
						|
   * get painted in what color]" />
 | 
						|
   *
 | 
						|
   * @param g the graphics into which the rectangle is drawn.
 | 
						|
   * @param x the x coordinate of the rectangle.
 | 
						|
   * @param y the y coordinate of the rectangle.
 | 
						|
   * @param width the width of the rectangle in pixels.
 | 
						|
   * @param height the height of the rectangle in pixels.
 | 
						|
   *
 | 
						|
   * @param isPressed <code>true</code> to draw the button border
 | 
						|
   *        with a pressed-in appearance; <code>false</code> for
 | 
						|
   *        normal (unpressed) appearance.
 | 
						|
   *
 | 
						|
   * @param isDefault <code>true</code> to draw the border with
 | 
						|
   *        the appearance it has when hitting the enter key in a
 | 
						|
   *        dialog will simulate a click to this button;
 | 
						|
   *        <code>false</code> for normal appearance.
 | 
						|
   *
 | 
						|
   * @param shadow the shadow color.
 | 
						|
   * @param darkShadow a darker variant of the shadow color.
 | 
						|
   * @param highlight the highlight color.
 | 
						|
   * @param lightHighlight a brighter variant of the highlight  color.
 | 
						|
   */
 | 
						|
  public static void drawBezel(Graphics g,
 | 
						|
                               int x, int y, int width, int height,
 | 
						|
                               boolean isPressed, boolean isDefault,
 | 
						|
                               Color shadow, Color darkShadow,
 | 
						|
                               Color highlight, Color lightHighlight)
 | 
						|
  {
 | 
						|
    Color oldColor = g.getColor();
 | 
						|
 | 
						|
    /* To understand this, it might be helpful to look at the image
 | 
						|
     * "BasicGraphicsUtils-3.png" that is included with the JavaDoc,
 | 
						|
     * and to compare it with "BasicGraphicsUtils-1.png" which shows
 | 
						|
     * the pixels painted by drawEtchedRect.  These image files are
 | 
						|
     * located in the "doc-files" subdirectory.
 | 
						|
     */
 | 
						|
    try
 | 
						|
    {
 | 
						|
      if ((isPressed == false) && (isDefault == false))
 | 
						|
      {
 | 
						|
        drawEtchedRect(g, x, y, width, height,
 | 
						|
                       lightHighlight, highlight,
 | 
						|
                       shadow, darkShadow);
 | 
						|
      }
 | 
						|
 | 
						|
      if ((isPressed == true) && (isDefault == false))
 | 
						|
      {
 | 
						|
        g.setColor(shadow);
 | 
						|
        g.drawRect(x + 1, y + 1, width - 2, height - 2);
 | 
						|
      }
 | 
						|
 | 
						|
      if ((isPressed == false) && (isDefault == true))
 | 
						|
      {
 | 
						|
        g.setColor(darkShadow);
 | 
						|
        g.drawRect(x, y, width - 1, height - 1);
 | 
						|
        drawEtchedRect(g, x + 1, y + 1, width - 2, height - 2,
 | 
						|
                       lightHighlight, highlight,
 | 
						|
                       shadow, darkShadow);
 | 
						|
      }
 | 
						|
 | 
						|
      if ((isPressed == true) && (isDefault == true))
 | 
						|
      {
 | 
						|
        g.setColor(darkShadow);
 | 
						|
        g.drawRect(x, y, width - 1, height - 1);
 | 
						|
        g.setColor(shadow);
 | 
						|
        g.drawRect(x + 1, y + 1, width - 3, height - 3);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    finally
 | 
						|
    {
 | 
						|
      g.setColor(oldColor);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a rectangle that appears lowered into the surface, given
 | 
						|
   * four colors that are used for drawing.
 | 
						|
   *
 | 
						|
   * <p><img src="doc-files/BasicGraphicsUtils-4.png" width="360"
 | 
						|
   * height="200" alt="[An illustration that shows which pixels
 | 
						|
   * get painted in what color]" />
 | 
						|
   *
 | 
						|
   * <p><strong>Compatibility with the Sun reference
 | 
						|
   * implementation:</strong> The Sun reference implementation seems
 | 
						|
   * to ignore the <code>x</code> and <code>y</code> arguments, at
 | 
						|
   * least in JDK 1.3.1 and 1.4.1_01.  The method always draws the
 | 
						|
   * rectangular area at location (0, 0). A bug report has been filed
 | 
						|
   * with Sun; its “bug ID” is 4880003.  The GNU Classpath
 | 
						|
   * implementation behaves correctly, thus not replicating this bug.
 | 
						|
   *
 | 
						|
   * @param g the graphics into which the rectangle is drawn.
 | 
						|
   * @param x the x coordinate of the rectangle.
 | 
						|
   * @param y the y coordinate of the rectangle.
 | 
						|
   * @param width the width of the rectangle in pixels.
 | 
						|
   * @param height the height of the rectangle in pixels.
 | 
						|
   *
 | 
						|
   * @param shadow the color that will be used for painting
 | 
						|
   *        the inner side of the top and left edges.
 | 
						|
   *
 | 
						|
   * @param darkShadow the color that will be used for painting
 | 
						|
   *        the outer side of the top and left edges.
 | 
						|
   *
 | 
						|
   * @param highlight the color that will be used for painting
 | 
						|
   *        the inner side of the bottom and right edges.
 | 
						|
   *
 | 
						|
   * @param lightHighlight the color that will be used for painting
 | 
						|
   *        the outer side of the bottom and right edges.
 | 
						|
   */
 | 
						|
  public static void drawLoweredBezel(Graphics g,
 | 
						|
                                      int x, int y, int width, int height,
 | 
						|
                                      Color shadow, Color darkShadow,
 | 
						|
                                      Color highlight, Color lightHighlight)
 | 
						|
  {
 | 
						|
    /* Like drawEtchedRect, but swapping darkShadow and shadow.
 | 
						|
     *
 | 
						|
     * To understand this, it might be helpful to look at the image
 | 
						|
     * "BasicGraphicsUtils-4.png" that is included with the JavaDoc,
 | 
						|
     * and to compare it with "BasicGraphicsUtils-1.png" which shows
 | 
						|
     * the pixels painted by drawEtchedRect.  These image files are
 | 
						|
     * located in the "doc-files" subdirectory.
 | 
						|
     */
 | 
						|
    drawEtchedRect(g, x, y, width, height,
 | 
						|
                   darkShadow, shadow,
 | 
						|
                   highlight, lightHighlight);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a String at the given location, underlining the first
 | 
						|
   * occurence of a specified character. The algorithm for determining
 | 
						|
   * the underlined position is not sensitive to case. If the
 | 
						|
   * character is not part of <code>text</code>, the text will be
 | 
						|
   * drawn without underlining. Drawing is performed in the current
 | 
						|
   * color and font of <code>g</code>.
 | 
						|
   *
 | 
						|
   * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
 | 
						|
   * height="100" alt="[An illustration showing how to use the
 | 
						|
   * method]" />
 | 
						|
   *
 | 
						|
   * @param g the graphics into which the String is drawn.
 | 
						|
   *
 | 
						|
   * @param text the String to draw.
 | 
						|
   *
 | 
						|
   * @param underlinedChar the character whose first occurence in
 | 
						|
   *        <code>text</code> will be underlined. It is not clear
 | 
						|
   *        why the API specification declares this argument to be
 | 
						|
   *        of type <code>int</code> instead of <code>char</code>.
 | 
						|
   *        While this would allow to pass Unicode characters outside
 | 
						|
   *        Basic Multilingual Plane 0 (U+0000 .. U+FFFE), at least
 | 
						|
   *        the GNU Classpath implementation does not underline
 | 
						|
   *        anything if <code>underlinedChar</code> is outside
 | 
						|
   *        the range of <code>char</code>.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate of the text, as it would be passed to
 | 
						|
   *        {@link java.awt.Graphics#drawString(java.lang.String,
 | 
						|
   *        int, int)}.
 | 
						|
   *
 | 
						|
   * @param y the y coordinate of the text, as it would be passed to
 | 
						|
   *        {@link java.awt.Graphics#drawString(java.lang.String,
 | 
						|
   *        int, int)}.
 | 
						|
   */
 | 
						|
  public static void drawString(Graphics g, String text,
 | 
						|
                                int underlinedChar, int x, int y)
 | 
						|
  {
 | 
						|
    int index = -1;
 | 
						|
 | 
						|
    /* It is intentional that lower case is used. In some languages,
 | 
						|
     * the set of lowercase characters is larger than the set of
 | 
						|
     * uppercase ones. Therefore, it is good practice to use lowercase
 | 
						|
     * for such comparisons (which really means that the author of this
 | 
						|
     * code can vaguely remember having read some Unicode techreport
 | 
						|
     * with this recommendation, but is too lazy to look for the URL).
 | 
						|
     */
 | 
						|
    if ((underlinedChar >= 0) || (underlinedChar <= 0xffff))
 | 
						|
      index = text.toLowerCase().indexOf(
 | 
						|
        Character.toLowerCase((char) underlinedChar));
 | 
						|
 | 
						|
    drawStringUnderlineCharAt(g, text, index, x, y);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a String at the given location, underlining the character
 | 
						|
   * at the specified index. Drawing is performed in the current color
 | 
						|
   * and font of <code>g</code>.
 | 
						|
   *
 | 
						|
   * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
 | 
						|
   * height="100" alt="[An illustration showing how to use the
 | 
						|
   * method]" />
 | 
						|
   *
 | 
						|
   * @param g the graphics into which the String is drawn.
 | 
						|
   *
 | 
						|
   * @param text the String to draw.
 | 
						|
   *
 | 
						|
   * @param underlinedIndex the index of the underlined character in
 | 
						|
   *        <code>text</code>.  If <code>underlinedIndex</code> falls
 | 
						|
   *        outside the range <code>[0, text.length() - 1]</code>, the
 | 
						|
   *        text will be drawn without underlining anything.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate of the text, as it would be passed to
 | 
						|
   *        {@link java.awt.Graphics#drawString(java.lang.String,
 | 
						|
   *        int, int)}.
 | 
						|
   *
 | 
						|
   * @param y the y coordinate of the text, as it would be passed to
 | 
						|
   *        {@link java.awt.Graphics#drawString(java.lang.String,
 | 
						|
   *        int, int)}.
 | 
						|
   *
 | 
						|
   * @since 1.4
 | 
						|
   */
 | 
						|
  public static void drawStringUnderlineCharAt(Graphics g, String text,
 | 
						|
                                               int underlinedIndex,
 | 
						|
                                               int x, int y)
 | 
						|
  {
 | 
						|
    Graphics2D g2;
 | 
						|
    Rectangle2D.Double underline;
 | 
						|
    FontRenderContext frc;
 | 
						|
    FontMetrics fmet;
 | 
						|
    LineMetrics lineMetrics;
 | 
						|
    Font font;
 | 
						|
    TextLayout layout;
 | 
						|
    double underlineX1, underlineX2;
 | 
						|
    boolean drawUnderline;
 | 
						|
    int textLength;
 | 
						|
 | 
						|
    textLength = text.length();
 | 
						|
    if (textLength == 0)
 | 
						|
      return;
 | 
						|
 | 
						|
    drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength);
 | 
						|
 | 
						|
    // FIXME: unfortunately pango and cairo can't agree on metrics
 | 
						|
    // so for the time being we continue to *not* use TextLayouts.
 | 
						|
    if (true || !(g instanceof Graphics2D))
 | 
						|
    {
 | 
						|
      /* Fall-back. This is likely to produce garbage for any text
 | 
						|
       * containing right-to-left (Hebrew or Arabic) characters, even
 | 
						|
       * if the underlined character is left-to-right.
 | 
						|
       */
 | 
						|
      g.drawString(text, x, y);
 | 
						|
      if (drawUnderline)
 | 
						|
      {
 | 
						|
        fmet = g.getFontMetrics();
 | 
						|
        g.fillRect(
 | 
						|
          /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)),
 | 
						|
          /* y */ y + fmet.getDescent() - 1,
 | 
						|
          /* width */ fmet.charWidth(text.charAt(underlinedIndex)),
 | 
						|
          /* height */ 1);
 | 
						|
      }
 | 
						|
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    g2 = (Graphics2D) g;
 | 
						|
    font = g2.getFont();
 | 
						|
    frc = g2.getFontRenderContext();
 | 
						|
    lineMetrics = font.getLineMetrics(text, frc);
 | 
						|
    layout = new TextLayout(text, font, frc);
 | 
						|
 | 
						|
    /* Draw the text. */
 | 
						|
    layout.draw(g2, x, y);
 | 
						|
    if (!drawUnderline)
 | 
						|
      return;
 | 
						|
 | 
						|
    underlineX1 = x + layout.getLogicalHighlightShape(
 | 
						|
     underlinedIndex, underlinedIndex).getBounds2D().getX();
 | 
						|
    underlineX2 = x + layout.getLogicalHighlightShape(
 | 
						|
     underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX();
 | 
						|
 | 
						|
    underline = new Rectangle2D.Double();
 | 
						|
    if (underlineX1 < underlineX2)
 | 
						|
    {
 | 
						|
      underline.x = underlineX1;
 | 
						|
      underline.width = underlineX2 - underlineX1;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      underline.x = underlineX2;
 | 
						|
      underline.width = underlineX1 - underlineX2;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    underline.height = lineMetrics.getUnderlineThickness();
 | 
						|
    underline.y = lineMetrics.getUnderlineOffset();
 | 
						|
    if (underline.y == 0)
 | 
						|
    {
 | 
						|
      /* Some fonts do not specify an underline offset, although they
 | 
						|
       * actually should do so. In that case, the result of calling
 | 
						|
       * lineMetrics.getUnderlineOffset() will be zero. Since it would
 | 
						|
       * look very ugly if the underline was be positioned immediately
 | 
						|
       * below the baseline, we check for this and move the underline
 | 
						|
       * below the descent, as shown in the following ASCII picture:
 | 
						|
       *
 | 
						|
       *   #####       ##### #
 | 
						|
       *  #     #     #     #
 | 
						|
       *  #     #     #     #
 | 
						|
       *  #     #     #     #
 | 
						|
       *   #####       ######        ---- baseline (0)
 | 
						|
       *                    #
 | 
						|
       *                    #
 | 
						|
       * ------------------###----------- lineMetrics.getDescent()
 | 
						|
       */
 | 
						|
      underline.y = lineMetrics.getDescent();
 | 
						|
    }
 | 
						|
 | 
						|
    underline.y += y;
 | 
						|
    g2.fill(underline);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a string on the specified component.
 | 
						|
   *
 | 
						|
   * @param c the component
 | 
						|
   * @param g the Graphics context
 | 
						|
   * @param text the string
 | 
						|
   * @param underlinedChar the character to be underlined
 | 
						|
   * @param x the X location
 | 
						|
   * @param y the Y location
 | 
						|
   */
 | 
						|
  static void drawString(JComponent c, Graphics g, String text,
 | 
						|
                                int underlinedChar, int x, int y)
 | 
						|
  {
 | 
						|
    int index = -1;
 | 
						|
 | 
						|
    /* It is intentional that lower case is used. In some languages,
 | 
						|
     * the set of lowercase characters is larger than the set of
 | 
						|
     * uppercase ones. Therefore, it is good practice to use lowercase
 | 
						|
     * for such comparisons (which really means that the author of this
 | 
						|
     * code can vaguely remember having read some Unicode techreport
 | 
						|
     * with this recommendation, but is too lazy to look for the URL).
 | 
						|
     */
 | 
						|
    if ((underlinedChar >= 0) || (underlinedChar <= 0xffff))
 | 
						|
      index = text.toLowerCase().indexOf(
 | 
						|
        Character.toLowerCase((char) underlinedChar));
 | 
						|
 | 
						|
    drawStringUnderlineCharAt(c, g, text, index, x, y);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a String at the given location, underlining the character
 | 
						|
   * at the specified index. Drawing is performed in the current color
 | 
						|
   * and font of <code>g</code>.
 | 
						|
   *
 | 
						|
   * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
 | 
						|
   * height="100" alt="[An illustration showing how to use the
 | 
						|
   * method]" />
 | 
						|
   *
 | 
						|
   * This is an accelerated version of the method with the same name. It
 | 
						|
   * uses a pre-laid out TextLayout stored in a client property.
 | 
						|
   *
 | 
						|
   * @param c the component that is drawn
 | 
						|
   * @param g the graphics into which the String is drawn.
 | 
						|
   *
 | 
						|
   * @param text the String to draw.
 | 
						|
   *
 | 
						|
   * @param underlinedIndex the index of the underlined character in
 | 
						|
   *        <code>text</code>.  If <code>underlinedIndex</code> falls
 | 
						|
   *        outside the range <code>[0, text.length() - 1]</code>, the
 | 
						|
   *        text will be drawn without underlining anything.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate of the text, as it would be passed to
 | 
						|
   *        {@link java.awt.Graphics#drawString(java.lang.String,
 | 
						|
   *        int, int)}.
 | 
						|
   *
 | 
						|
   * @param y the y coordinate of the text, as it would be passed to
 | 
						|
   *        {@link java.awt.Graphics#drawString(java.lang.String,
 | 
						|
   *        int, int)}.
 | 
						|
   */
 | 
						|
  static void drawStringUnderlineCharAt(JComponent c, Graphics g, String text,
 | 
						|
                                        int underlinedIndex,
 | 
						|
                                        int x, int y)
 | 
						|
  {
 | 
						|
    Graphics2D g2;
 | 
						|
    Rectangle2D.Double underline;
 | 
						|
    FontRenderContext frc;
 | 
						|
    FontMetrics fmet;
 | 
						|
    LineMetrics lineMetrics;
 | 
						|
    Font font;
 | 
						|
    TextLayout layout;
 | 
						|
    double underlineX1, underlineX2;
 | 
						|
    boolean drawUnderline;
 | 
						|
    int textLength;
 | 
						|
 | 
						|
    textLength = text.length();
 | 
						|
    if (textLength == 0)
 | 
						|
      return;
 | 
						|
 | 
						|
    drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength);
 | 
						|
 | 
						|
    // FIXME: unfortunately pango and cairo can't agree on metrics
 | 
						|
    // so for the time being we continue to *not* use TextLayouts.
 | 
						|
    if (!(g instanceof Graphics2D)
 | 
						|
       || SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") != null)
 | 
						|
    {
 | 
						|
      /* Fall-back. This is likely to produce garbage for any text
 | 
						|
       * containing right-to-left (Hebrew or Arabic) characters, even
 | 
						|
       * if the underlined character is left-to-right.
 | 
						|
       */
 | 
						|
      g.drawString(text, x, y);
 | 
						|
      if (drawUnderline)
 | 
						|
      {
 | 
						|
        fmet = g.getFontMetrics();
 | 
						|
        g.fillRect(
 | 
						|
          /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)),
 | 
						|
          /* y */ y + 1,
 | 
						|
          /* width */ fmet.charWidth(text.charAt(underlinedIndex)),
 | 
						|
          /* height */ 1);
 | 
						|
      }
 | 
						|
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    g2 = (Graphics2D) g;
 | 
						|
    font = g2.getFont();
 | 
						|
    frc = g2.getFontRenderContext();
 | 
						|
    lineMetrics = font.getLineMetrics(text, frc);
 | 
						|
    layout = (TextLayout) c.getClientProperty(CACHED_TEXT_LAYOUT);
 | 
						|
    if (layout == null)
 | 
						|
      {
 | 
						|
        layout = new TextLayout(text, font, frc);
 | 
						|
        System.err.println("Unable to use cached TextLayout for: " + text);
 | 
						|
      }
 | 
						|
 | 
						|
    /* Draw the text. */
 | 
						|
    layout.draw(g2, x, y);
 | 
						|
    if (!drawUnderline)
 | 
						|
      return;
 | 
						|
 | 
						|
    underlineX1 = x + layout.getLogicalHighlightShape(
 | 
						|
     underlinedIndex, underlinedIndex).getBounds2D().getX();
 | 
						|
    underlineX2 = x + layout.getLogicalHighlightShape(
 | 
						|
     underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX();
 | 
						|
 | 
						|
    underline = new Rectangle2D.Double();
 | 
						|
    if (underlineX1 < underlineX2)
 | 
						|
    {
 | 
						|
      underline.x = underlineX1;
 | 
						|
      underline.width = underlineX2 - underlineX1;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      underline.x = underlineX2;
 | 
						|
      underline.width = underlineX1 - underlineX2;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    underline.height = lineMetrics.getUnderlineThickness();
 | 
						|
    underline.y = lineMetrics.getUnderlineOffset();
 | 
						|
    if (underline.y == 0)
 | 
						|
    {
 | 
						|
      /* Some fonts do not specify an underline offset, although they
 | 
						|
       * actually should do so. In that case, the result of calling
 | 
						|
       * lineMetrics.getUnderlineOffset() will be zero. Since it would
 | 
						|
       * look very ugly if the underline was be positioned immediately
 | 
						|
       * below the baseline, we check for this and move the underline
 | 
						|
       * below the descent, as shown in the following ASCII picture:
 | 
						|
       *
 | 
						|
       *   #####       ##### #
 | 
						|
       *  #     #     #     #
 | 
						|
       *  #     #     #     #
 | 
						|
       *  #     #     #     #
 | 
						|
       *   #####       ######        ---- baseline (0)
 | 
						|
       *                    #
 | 
						|
       *                    #
 | 
						|
       * ------------------###----------- lineMetrics.getDescent()
 | 
						|
       */
 | 
						|
      underline.y = lineMetrics.getDescent();
 | 
						|
    }
 | 
						|
 | 
						|
    underline.y += y;
 | 
						|
    g2.fill(underline);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a rectangle, simulating a dotted stroke by painting only
 | 
						|
   * every second pixel along the one-pixel thick edge. The color of
 | 
						|
   * those pixels is the current color of the Graphics <code>g</code>.
 | 
						|
   * Any other pixels are left unchanged.
 | 
						|
   *
 | 
						|
   * <p><img src="doc-files/BasicGraphicsUtils-7.png" width="360"
 | 
						|
   * height="200" alt="[An illustration that shows which pixels
 | 
						|
   * get painted]" />
 | 
						|
   *
 | 
						|
   * @param g the graphics into which the rectangle is drawn.
 | 
						|
   * @param x the x coordinate of the rectangle.
 | 
						|
   * @param y the y coordinate of the rectangle.
 | 
						|
   * @param width the width of the rectangle in pixels.
 | 
						|
   * @param height the height of the rectangle in pixels.
 | 
						|
   */
 | 
						|
  public static void drawDashedRect(Graphics g,
 | 
						|
                                    int x, int y, int width, int height)
 | 
						|
  {
 | 
						|
    int right = x + width - 1;
 | 
						|
    int bottom = y + height - 1;
 | 
						|
 | 
						|
    /* Draw the top and bottom edge of the dotted rectangle. */
 | 
						|
    for (int i = x; i <= right; i += 2)
 | 
						|
    {
 | 
						|
      g.drawLine(i, y, i, y);
 | 
						|
      g.drawLine(i, bottom, i, bottom);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Draw the left and right edge of the dotted rectangle. */
 | 
						|
    for (int i = y; i <= bottom; i += 2)
 | 
						|
    {
 | 
						|
      g.drawLine(x, i, x, i);
 | 
						|
      g.drawLine(right, i, right, i);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Determines the preferred width and height of an AbstractButton,
 | 
						|
   * given the gap between the button’s text and icon.
 | 
						|
   *
 | 
						|
   * @param b the button whose preferred size is determined.
 | 
						|
   *
 | 
						|
   * @param textIconGap the gap between the button’s text and
 | 
						|
   *        icon.
 | 
						|
   *
 | 
						|
   * @return a <code>Dimension</code> object whose <code>width</code>
 | 
						|
   *         and <code>height</code> fields indicate the preferred
 | 
						|
   *         extent in pixels.
 | 
						|
   *
 | 
						|
   * @see javax.swing.SwingUtilities#layoutCompoundLabel(JComponent,
 | 
						|
   *      FontMetrics, String, Icon, int, int, int, int, Rectangle, Rectangle,
 | 
						|
   *      Rectangle, int)
 | 
						|
   */
 | 
						|
  public static Dimension getPreferredButtonSize(AbstractButton b,
 | 
						|
                                                 int textIconGap)
 | 
						|
  {
 | 
						|
    // These cached rectangles are use here and in BasicButtonUI.paint(),
 | 
						|
    // so these two methods must never be executed concurrently. Maybe
 | 
						|
    // we must use other Rectangle instances here. OTOH, Swing is
 | 
						|
    // designed to be not thread safe, and every layout and paint operation
 | 
						|
    // should be performed from the EventDispatchThread, so it _should_ be
 | 
						|
    // OK to do this optimization.
 | 
						|
    Rectangle viewRect = BasicButtonUI.viewR;
 | 
						|
    viewRect.x = 0;
 | 
						|
    viewRect.y = 0;
 | 
						|
    viewRect.width = Short.MAX_VALUE;
 | 
						|
    viewRect.height = Short.MAX_VALUE;
 | 
						|
    Rectangle iconRect = BasicButtonUI.iconR;
 | 
						|
    iconRect.x = 0;
 | 
						|
    iconRect.y = 0;
 | 
						|
    iconRect.width = 0;
 | 
						|
    iconRect.height = 0;
 | 
						|
    Rectangle textRect = BasicButtonUI.textR;
 | 
						|
    textRect.x = 0;
 | 
						|
    textRect.y = 0;
 | 
						|
    textRect.width = 0;
 | 
						|
    textRect.height = 0;
 | 
						|
 | 
						|
   SwingUtilities.layoutCompoundLabel(
 | 
						|
      b, // for the component orientation
 | 
						|
      b.getFontMetrics(b.getFont()), // see comment above
 | 
						|
      b.getText(),
 | 
						|
      b.getIcon(),
 | 
						|
      b.getVerticalAlignment(),
 | 
						|
      b.getHorizontalAlignment(),
 | 
						|
      b.getVerticalTextPosition(),
 | 
						|
      b.getHorizontalTextPosition(),
 | 
						|
      viewRect, iconRect, textRect,
 | 
						|
      textIconGap);
 | 
						|
 | 
						|
    /*  +------------------------+       +------------------------+
 | 
						|
     *  |                        |       |                        |
 | 
						|
     *  | ICON                   |       | CONTENTCONTENTCONTENT  |
 | 
						|
     *  |          TEXTTEXTTEXT  |  -->  | CONTENTCONTENTCONTENT  |
 | 
						|
     *  |          TEXTTEXTTEXT  |       | CONTENTCONTENTCONTENT  |
 | 
						|
     *  +------------------------+       +------------------------+
 | 
						|
     */
 | 
						|
 | 
						|
    Rectangle contentRect =
 | 
						|
      SwingUtilities.computeUnion(textRect.x, textRect.y, textRect.width,
 | 
						|
                                  textRect.height, iconRect);
 | 
						|
 | 
						|
    Insets insets = b.getInsets();
 | 
						|
    return new Dimension(insets.left + contentRect.width + insets.right,
 | 
						|
                         insets.top + contentRect.height + insets.bottom);
 | 
						|
  }
 | 
						|
}
 |