| /* 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 + 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 = (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) |
| { |
| Rectangle contentRect; |
| Rectangle viewRect; |
| Rectangle iconRect = new Rectangle(); |
| Rectangle textRect = new Rectangle(); |
| Insets insets = b.getInsets(); |
| |
| viewRect = new Rectangle(); |
| |
| /* java.awt.Toolkit.getFontMetrics is deprecated. However, it |
| * seems not obvious how to get to the correct FontMetrics object |
| * otherwise. The real problem probably is that the method |
| * javax.swing.SwingUtilities.layoutCompundLabel should take a |
| * LineMetrics, not a FontMetrics argument. But fixing this that |
| * would change the public API. |
| */ |
| SwingUtilities.layoutCompoundLabel( |
| b, // for the component orientation |
| b.getToolkit().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 | |
| * +------------------------+ +------------------------+ |
| */ |
| |
| contentRect = textRect.union(iconRect); |
| |
| return new Dimension(insets.left |
| + contentRect.width |
| + insets.right + b.getHorizontalAlignment(), |
| insets.top |
| + contentRect.height |
| + insets.bottom); |
| } |
| } |