Dead
[official-gcc.git] / gomp-20050608-branch / libjava / classpath / javax / swing / plaf / basic / BasicGraphicsUtils.java
blob068de345bec3ccac4444e75521ad9e3f2c2239c1
1 /* BasicGraphicsUtils.java
2 Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package javax.swing.plaf.basic;
40 import java.awt.Color;
41 import java.awt.Dimension;
42 import java.awt.Font;
43 import java.awt.FontMetrics;
44 import java.awt.Graphics;
45 import java.awt.Graphics2D;
46 import java.awt.Insets;
47 import java.awt.Rectangle;
48 import java.awt.font.FontRenderContext;
49 import java.awt.font.LineMetrics;
50 import java.awt.font.TextLayout;
51 import java.awt.geom.Rectangle2D;
53 import javax.swing.AbstractButton;
54 import javax.swing.Icon;
55 import javax.swing.JComponent;
56 import javax.swing.SwingUtilities;
59 /**
60 * A utility class providing commonly used drawing and measurement
61 * routines.
63 * @author Sascha Brawer (brawer@dandelis.ch)
65 public class BasicGraphicsUtils
67 /**
68 * Constructor. It is utterly unclear why this class should
69 * be constructable, but this is what the API specification
70 * says.
72 public BasicGraphicsUtils()
74 // Nothing to do here.
78 /**
79 * Draws a rectangle that appears etched into the surface, given
80 * four colors that are used for drawing.
82 * <p><img src="doc-files/BasicGraphicsUtils-1.png" width="360"
83 * height="200" alt="[An illustration that shows which pixels
84 * get painted in what color]" />
86 * @param g the graphics into which the rectangle is drawn.
87 * @param x the x coordinate of the rectangle.
88 * @param y the y coordinate of the rectangle.
89 * @param width the width of the rectangle in pixels.
90 * @param height the height of the rectangle in pixels.
92 * @param shadow the color that will be used for painting
93 * the outer side of the top and left edges.
95 * @param darkShadow the color that will be used for painting
96 * the inner side of the top and left edges.
98 * @param highlight the color that will be used for painting
99 * the inner side of the bottom and right edges.
101 * @param lightHighlight the color that will be used for painting
102 * the outer side of the bottom and right edges.
104 * @see #getEtchedInsets()
105 * @see javax.swing.border.EtchedBorder
107 public static void drawEtchedRect(Graphics g,
108 int x, int y, int width, int height,
109 Color shadow, Color darkShadow,
110 Color highlight, Color lightHighlight)
112 Color oldColor;
113 int x2, y2;
115 oldColor = g.getColor();
116 x2 = x + width - 1;
117 y2 = y + height - 1;
121 /* To understand this code, it might be helpful to look at the
122 * image "BasicGraphicsUtils-1.png" that is included with the
123 * JavaDoc. The file is located in the "doc-files" subdirectory.
125 * (x2, y2) is the coordinate of the most right and bottom pixel
126 * to be painted.
128 g.setColor(shadow);
129 g.drawLine(x, y, x2 - 1, y); // top, outer
130 g.drawLine(x, y + 1, x, y2 - 1); // left, outer
132 g.setColor(darkShadow);
133 g.drawLine(x + 1, y + 1, x2 - 2, y + 1); // top, inner
134 g.drawLine(x + 1, y + 2, x + 1, y2 - 2); // left, inner
136 g.setColor(highlight);
137 g.drawLine(x + 1, y2 - 1, x2 - 1, y2 - 1); // bottom, inner
138 g.drawLine(x2 - 1, y + 1, x2 - 1, y2 - 2); // right, inner
140 g.setColor(lightHighlight);
141 g.drawLine(x, y2, x2, y2); // bottom, outer
142 g.drawLine(x2, y, x2, y2 - 1); // right, outer
144 finally
146 g.setColor(oldColor);
152 * Determines the width of the border that gets painted by
153 * {@link #drawEtchedRect}.
155 * @return an <code>Insets</code> object whose <code>top</code>,
156 * <code>left</code>, <code>bottom</code> and
157 * <code>right</code> field contain the border width at the
158 * respective edge in pixels.
160 public static Insets getEtchedInsets()
162 return new Insets(2, 2, 2, 2);
167 * Draws a rectangle that appears etched into the surface, given
168 * two colors that are used for drawing.
170 * <p><img src="doc-files/BasicGraphicsUtils-2.png" width="360"
171 * height="200" alt="[An illustration that shows which pixels
172 * get painted in what color]" />
174 * @param g the graphics into which the rectangle is drawn.
175 * @param x the x coordinate of the rectangle.
176 * @param y the y coordinate of the rectangle.
177 * @param width the width of the rectangle in pixels.
178 * @param height the height of the rectangle in pixels.
180 * @param shadow the color that will be used for painting the outer
181 * side of the top and left edges, and for the inner side of
182 * the bottom and right ones.
184 * @param highlight the color that will be used for painting the
185 * inner side of the top and left edges, and for the outer
186 * side of the bottom and right ones.
188 * @see #getGrooveInsets()
189 * @see javax.swing.border.EtchedBorder
191 public static void drawGroove(Graphics g,
192 int x, int y, int width, int height,
193 Color shadow, Color highlight)
195 /* To understand this, it might be helpful to look at the image
196 * "BasicGraphicsUtils-2.png" that is included with the JavaDoc,
197 * and to compare it with "BasicGraphicsUtils-1.png" which shows
198 * the pixels painted by drawEtchedRect. These image files are
199 * located in the "doc-files" subdirectory.
201 drawEtchedRect(g, x, y, width, height,
202 /* outer topLeft */ shadow,
203 /* inner topLeft */ highlight,
204 /* inner bottomRight */ shadow,
205 /* outer bottomRight */ highlight);
210 * Determines the width of the border that gets painted by
211 * {@link #drawGroove}.
213 * @return an <code>Insets</code> object whose <code>top</code>,
214 * <code>left</code>, <code>bottom</code> and
215 * <code>right</code> field contain the border width at the
216 * respective edge in pixels.
218 public static Insets getGrooveInsets()
220 return new Insets(2, 2, 2, 2);
225 * Draws a border that is suitable for buttons of the Basic look and
226 * feel.
228 * <p><img src="doc-files/BasicGraphicsUtils-3.png" width="500"
229 * height="300" alt="[An illustration that shows which pixels
230 * get painted in what color]" />
232 * @param g the graphics into which the rectangle is drawn.
233 * @param x the x coordinate of the rectangle.
234 * @param y the y coordinate of the rectangle.
235 * @param width the width of the rectangle in pixels.
236 * @param height the height of the rectangle in pixels.
238 * @param isPressed <code>true</code> to draw the button border
239 * with a pressed-in appearance; <code>false</code> for
240 * normal (unpressed) appearance.
242 * @param isDefault <code>true</code> to draw the border with
243 * the appearance it has when hitting the enter key in a
244 * dialog will simulate a click to this button;
245 * <code>false</code> for normal appearance.
247 * @param shadow the shadow color.
248 * @param darkShadow a darker variant of the shadow color.
249 * @param highlight the highlight color.
250 * @param lightHighlight a brighter variant of the highlight color.
252 public static void drawBezel(Graphics g,
253 int x, int y, int width, int height,
254 boolean isPressed, boolean isDefault,
255 Color shadow, Color darkShadow,
256 Color highlight, Color lightHighlight)
258 Color oldColor = g.getColor();
260 /* To understand this, it might be helpful to look at the image
261 * "BasicGraphicsUtils-3.png" that is included with the JavaDoc,
262 * and to compare it with "BasicGraphicsUtils-1.png" which shows
263 * the pixels painted by drawEtchedRect. These image files are
264 * located in the "doc-files" subdirectory.
268 if ((isPressed == false) && (isDefault == false))
270 drawEtchedRect(g, x, y, width, height,
271 lightHighlight, highlight,
272 shadow, darkShadow);
275 if ((isPressed == true) && (isDefault == false))
277 g.setColor(shadow);
278 g.drawRect(x + 1, y + 1, width - 2, height - 2);
281 if ((isPressed == false) && (isDefault == true))
283 g.setColor(darkShadow);
284 g.drawRect(x, y, width - 1, height - 1);
285 drawEtchedRect(g, x + 1, y + 1, width - 2, height - 2,
286 lightHighlight, highlight,
287 shadow, darkShadow);
290 if ((isPressed == true) && (isDefault == true))
292 g.setColor(darkShadow);
293 g.drawRect(x, y, width - 1, height - 1);
294 g.setColor(shadow);
295 g.drawRect(x + 1, y + 1, width - 3, height - 3);
298 finally
300 g.setColor(oldColor);
306 * Draws a rectangle that appears lowered into the surface, given
307 * four colors that are used for drawing.
309 * <p><img src="doc-files/BasicGraphicsUtils-4.png" width="360"
310 * height="200" alt="[An illustration that shows which pixels
311 * get painted in what color]" />
313 * <p><strong>Compatibility with the Sun reference
314 * implementation:</strong> The Sun reference implementation seems
315 * to ignore the <code>x</code> and <code>y</code> arguments, at
316 * least in JDK 1.3.1 and 1.4.1_01. The method always draws the
317 * rectangular area at location (0, 0). A bug report has been filed
318 * with Sun; its &#x201c;bug ID&#x201d; is 4880003. The GNU Classpath
319 * implementation behaves correctly, thus not replicating this bug.
321 * @param g the graphics into which the rectangle is drawn.
322 * @param x the x coordinate of the rectangle.
323 * @param y the y coordinate of the rectangle.
324 * @param width the width of the rectangle in pixels.
325 * @param height the height of the rectangle in pixels.
327 * @param shadow the color that will be used for painting
328 * the inner side of the top and left edges.
330 * @param darkShadow the color that will be used for painting
331 * the outer side of the top and left edges.
333 * @param highlight the color that will be used for painting
334 * the inner side of the bottom and right edges.
336 * @param lightHighlight the color that will be used for painting
337 * the outer side of the bottom and right edges.
339 public static void drawLoweredBezel(Graphics g,
340 int x, int y, int width, int height,
341 Color shadow, Color darkShadow,
342 Color highlight, Color lightHighlight)
344 /* Like drawEtchedRect, but swapping darkShadow and shadow.
346 * To understand this, it might be helpful to look at the image
347 * "BasicGraphicsUtils-4.png" that is included with the JavaDoc,
348 * and to compare it with "BasicGraphicsUtils-1.png" which shows
349 * the pixels painted by drawEtchedRect. These image files are
350 * located in the "doc-files" subdirectory.
352 drawEtchedRect(g, x, y, width, height,
353 darkShadow, shadow,
354 highlight, lightHighlight);
359 * Draws a String at the given location, underlining the first
360 * occurence of a specified character. The algorithm for determining
361 * the underlined position is not sensitive to case. If the
362 * character is not part of <code>text</code>, the text will be
363 * drawn without underlining. Drawing is performed in the current
364 * color and font of <code>g</code>.
366 * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
367 * height="100" alt="[An illustration showing how to use the
368 * method]" />
370 * @param g the graphics into which the String is drawn.
372 * @param text the String to draw.
374 * @param underlinedChar the character whose first occurence in
375 * <code>text</code> will be underlined. It is not clear
376 * why the API specification declares this argument to be
377 * of type <code>int</code> instead of <code>char</code>.
378 * While this would allow to pass Unicode characters outside
379 * Basic Multilingual Plane 0 (U+0000 .. U+FFFE), at least
380 * the GNU Classpath implementation does not underline
381 * anything if <code>underlinedChar</code> is outside
382 * the range of <code>char</code>.
384 * @param x the x coordinate of the text, as it would be passed to
385 * {@link java.awt.Graphics#drawString(java.lang.String,
386 * int, int)}.
388 * @param y the y coordinate of the text, as it would be passed to
389 * {@link java.awt.Graphics#drawString(java.lang.String,
390 * int, int)}.
392 public static void drawString(Graphics g, String text,
393 int underlinedChar, int x, int y)
395 int index = -1;
397 /* It is intentional that lower case is used. In some languages,
398 * the set of lowercase characters is larger than the set of
399 * uppercase ones. Therefore, it is good practice to use lowercase
400 * for such comparisons (which really means that the author of this
401 * code can vaguely remember having read some Unicode techreport
402 * with this recommendation, but is too lazy to look for the URL).
404 if ((underlinedChar >= 0) || (underlinedChar <= 0xffff))
405 index = text.toLowerCase().indexOf(
406 Character.toLowerCase((char) underlinedChar));
408 drawStringUnderlineCharAt(g, text, index, x, y);
413 * Draws a String at the given location, underlining the character
414 * at the specified index. Drawing is performed in the current color
415 * and font of <code>g</code>.
417 * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
418 * height="100" alt="[An illustration showing how to use the
419 * method]" />
421 * @param g the graphics into which the String is drawn.
423 * @param text the String to draw.
425 * @param underlinedIndex the index of the underlined character in
426 * <code>text</code>. If <code>underlinedIndex</code> falls
427 * outside the range <code>[0, text.length() - 1]</code>, the
428 * text will be drawn without underlining anything.
430 * @param x the x coordinate of the text, as it would be passed to
431 * {@link java.awt.Graphics#drawString(java.lang.String,
432 * int, int)}.
434 * @param y the y coordinate of the text, as it would be passed to
435 * {@link java.awt.Graphics#drawString(java.lang.String,
436 * int, int)}.
438 * @since 1.4
440 public static void drawStringUnderlineCharAt(Graphics g, String text,
441 int underlinedIndex,
442 int x, int y)
444 Graphics2D g2;
445 Rectangle2D.Double underline;
446 FontRenderContext frc;
447 FontMetrics fmet;
448 LineMetrics lineMetrics;
449 Font font;
450 TextLayout layout;
451 double underlineX1, underlineX2;
452 boolean drawUnderline;
453 int textLength;
455 textLength = text.length();
456 if (textLength == 0)
457 return;
459 drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength);
461 // FIXME: unfortunately pango and cairo can't agree on metrics
462 // so for the time being we continue to *not* use TextLayouts.
463 if (true || !(g instanceof Graphics2D))
465 /* Fall-back. This is likely to produce garbage for any text
466 * containing right-to-left (Hebrew or Arabic) characters, even
467 * if the underlined character is left-to-right.
469 g.drawString(text, x, y);
470 if (drawUnderline)
472 fmet = g.getFontMetrics();
473 g.fillRect(
474 /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)),
475 /* y */ y + fmet.getDescent() - 1,
476 /* width */ fmet.charWidth(text.charAt(underlinedIndex)),
477 /* height */ 1);
480 return;
483 g2 = (Graphics2D) g;
484 font = g2.getFont();
485 frc = g2.getFontRenderContext();
486 lineMetrics = font.getLineMetrics(text, frc);
487 layout = new TextLayout(text, font, frc);
489 /* Draw the text. */
490 layout.draw(g2, x, y);
491 if (!drawUnderline)
492 return;
494 underlineX1 = x + layout.getLogicalHighlightShape(
495 underlinedIndex, underlinedIndex).getBounds2D().getX();
496 underlineX2 = x + layout.getLogicalHighlightShape(
497 underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX();
499 underline = new Rectangle2D.Double();
500 if (underlineX1 < underlineX2)
502 underline.x = underlineX1;
503 underline.width = underlineX2 - underlineX1;
505 else
507 underline.x = underlineX2;
508 underline.width = underlineX1 - underlineX2;
512 underline.height = lineMetrics.getUnderlineThickness();
513 underline.y = lineMetrics.getUnderlineOffset();
514 if (underline.y == 0)
516 /* Some fonts do not specify an underline offset, although they
517 * actually should do so. In that case, the result of calling
518 * lineMetrics.getUnderlineOffset() will be zero. Since it would
519 * look very ugly if the underline was be positioned immediately
520 * below the baseline, we check for this and move the underline
521 * below the descent, as shown in the following ASCII picture:
523 * ##### ##### #
524 * # # # #
525 * # # # #
526 * # # # #
527 * ##### ###### ---- baseline (0)
530 * ------------------###----------- lineMetrics.getDescent()
532 underline.y = lineMetrics.getDescent();
535 underline.y += y;
536 g2.fill(underline);
541 * Draws a rectangle, simulating a dotted stroke by painting only
542 * every second pixel along the one-pixel thick edge. The color of
543 * those pixels is the current color of the Graphics <code>g</code>.
544 * Any other pixels are left unchanged.
546 * <p><img src="doc-files/BasicGraphicsUtils-7.png" width="360"
547 * height="200" alt="[An illustration that shows which pixels
548 * get painted]" />
550 * @param g the graphics into which the rectangle is drawn.
551 * @param x the x coordinate of the rectangle.
552 * @param y the y coordinate of the rectangle.
553 * @param width the width of the rectangle in pixels.
554 * @param height the height of the rectangle in pixels.
556 public static void drawDashedRect(Graphics g,
557 int x, int y, int width, int height)
559 int right = x + width - 1;
560 int bottom = y + height - 1;
562 /* Draw the top and bottom edge of the dotted rectangle. */
563 for (int i = x; i <= right; i += 2)
565 g.drawLine(i, y, i, y);
566 g.drawLine(i, bottom, i, bottom);
569 /* Draw the left and right edge of the dotted rectangle. */
570 for (int i = y; i <= bottom; i += 2)
572 g.drawLine(x, i, x, i);
573 g.drawLine(right, i, right, i);
579 * Determines the preferred width and height of an AbstractButton,
580 * given the gap between the button&#x2019;s text and icon.
582 * @param b the button whose preferred size is determined.
584 * @param textIconGap the gap between the button&#x2019;s text and
585 * icon.
587 * @return a <code>Dimension</code> object whose <code>width</code>
588 * and <code>height</code> fields indicate the preferred
589 * extent in pixels.
591 * @see javax.swing.SwingUtilities#layoutCompoundLabel(JComponent,
592 * FontMetrics, String, Icon, int, int, int, int, Rectangle, Rectangle,
593 * Rectangle, int)
595 public static Dimension getPreferredButtonSize(AbstractButton b,
596 int textIconGap)
598 Rectangle contentRect;
599 Rectangle viewRect;
600 Rectangle iconRect = new Rectangle();
601 Rectangle textRect = new Rectangle();
602 Insets insets = b.getInsets();
604 viewRect = new Rectangle();
606 /* java.awt.Toolkit.getFontMetrics is deprecated. However, it
607 * seems not obvious how to get to the correct FontMetrics object
608 * otherwise. The real problem probably is that the method
609 * javax.swing.SwingUtilities.layoutCompundLabel should take a
610 * LineMetrics, not a FontMetrics argument. But fixing this that
611 * would change the public API.
613 SwingUtilities.layoutCompoundLabel(
614 b, // for the component orientation
615 b.getToolkit().getFontMetrics(b.getFont()), // see comment above
616 b.getText(),
617 b.getIcon(),
618 b.getVerticalAlignment(),
619 b.getHorizontalAlignment(),
620 b.getVerticalTextPosition(),
621 b.getHorizontalTextPosition(),
622 viewRect, iconRect, textRect,
623 textIconGap);
625 /* +------------------------+ +------------------------+
626 * | | | |
627 * | ICON | | CONTENTCONTENTCONTENT |
628 * | TEXTTEXTTEXT | --> | CONTENTCONTENTCONTENT |
629 * | TEXTTEXTTEXT | | CONTENTCONTENTCONTENT |
630 * +------------------------+ +------------------------+
633 contentRect = textRect.union(iconRect);
635 return new Dimension(insets.left
636 + contentRect.width
637 + insets.right + b.getHorizontalAlignment(),
638 insets.top
639 + contentRect.height
640 + insets.bottom);