Source for javax.swing.plaf.basic.BasicGraphicsUtils

   1: /* BasicGraphicsUtils.java
   2:    Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   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.
  10: 
  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.
  15: 
  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.
  20: 
  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.
  25: 
  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. */
  37: 
  38: package javax.swing.plaf.basic;
  39: 
  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;
  52: 
  53: import javax.swing.AbstractButton;
  54: import javax.swing.Icon;
  55: import javax.swing.JComponent;
  56: import javax.swing.SwingUtilities;
  57: 
  58: 
  59: /**
  60:  * A utility class providing commonly used drawing and measurement
  61:  * routines.
  62:  *
  63:  * @author Sascha Brawer (brawer@dandelis.ch)
  64:  */
  65: public class BasicGraphicsUtils
  66: {
  67:   /**
  68:    * Constructor. It is utterly unclear why this class should
  69:    * be constructable, but this is what the API specification
  70:    * says.
  71:    */
  72:   public BasicGraphicsUtils()
  73:   {
  74:   }
  75: 
  76: 
  77:   /**
  78:    * Draws a rectangle that appears etched into the surface, given
  79:    * four colors that are used for drawing.
  80:    *
  81:    * <p><img src="doc-files/BasicGraphicsUtils-1.png" width="360"
  82:    * height="200" alt="[An illustration that shows which pixels
  83:    * get painted in what color]" />
  84:    *
  85:    * @param g the graphics into which the rectangle is drawn.
  86:    * @param x the x coordinate of the rectangle.
  87:    * @param y the y coordinate of the rectangle.
  88:    * @param width the width of the rectangle in pixels.
  89:    * @param height the height of the rectangle in pixels.
  90:    *
  91:    * @param shadow the color that will be used for painting
  92:    *        the outer side of the top and left edges.
  93:    *
  94:    * @param darkShadow the color that will be used for painting
  95:    *        the inner side of the top and left edges.
  96:    *
  97:    * @param highlight the color that will be used for painting
  98:    *        the inner side of the bottom and right edges.
  99:    *
 100:    * @param lightHighlight the color that will be used for painting
 101:    *        the outer side of the bottom and right edges.
 102:    *
 103:    * @see #getEtchedInsets()
 104:    * @see javax.swing.border.EtchedBorder
 105:    */
 106:   public static void drawEtchedRect(Graphics g,
 107:                                     int x, int y, int width, int height,
 108:                                     Color shadow, Color darkShadow,
 109:                                     Color highlight, Color lightHighlight)
 110:   {
 111:     Color oldColor;
 112:     int x2, y2;
 113: 
 114:     oldColor = g.getColor();
 115:     x2 = x + width - 1;
 116:     y2 = y + height - 1;
 117: 
 118:     try
 119:     {
 120:       /* To understand this code, it might be helpful to look at the
 121:        * image "BasicGraphicsUtils-1.png" that is included with the
 122:        * JavaDoc. The file is located in the "doc-files" subdirectory.
 123:        *
 124:        * (x2, y2) is the coordinate of the most right and bottom pixel
 125:        * to be painted.
 126:        */
 127:       g.setColor(shadow);
 128:       g.drawLine(x, y, x2 - 1, y);                     // top, outer
 129:       g.drawLine(x, y + 1, x, y2 - 1);                 // left, outer
 130: 
 131:       g.setColor(darkShadow);
 132:       g.drawLine(x + 1, y + 1, x2 - 2, y + 1);         // top, inner
 133:       g.drawLine(x + 1, y + 2, x + 1, y2 - 2);         // left, inner
 134:       
 135:       g.setColor(highlight);
 136:       g.drawLine(x + 1, y2 - 1, x2 - 1, y2 - 1);       // bottom, inner
 137:       g.drawLine(x2 - 1, y + 1, x2 - 1, y2 - 2);       // right, inner
 138: 
 139:       g.setColor(lightHighlight);
 140:       g.drawLine(x, y2, x2, y2);                       // bottom, outer
 141:       g.drawLine(x2, y, x2, y2 - 1);                   // right, outer
 142:     }
 143:     finally
 144:     {
 145:       g.setColor(oldColor);
 146:     }
 147:   }
 148:   
 149:   
 150:   /**
 151:    * Determines the width of the border that gets painted by
 152:    * {@link #drawEtchedRect}.
 153:    *
 154:    * @return an <code>Insets</code> object whose <code>top</code>,
 155:    *         <code>left</code>, <code>bottom</code> and
 156:    *         <code>right</code> field contain the border width at the
 157:    *         respective edge in pixels.
 158:    */
 159:   public static Insets getEtchedInsets()
 160:   {
 161:     return new Insets(2, 2, 2, 2);
 162:   }
 163: 
 164: 
 165:   /**
 166:    * Draws a rectangle that appears etched into the surface, given
 167:    * two colors that are used for drawing.
 168:    *
 169:    * <p><img src="doc-files/BasicGraphicsUtils-2.png" width="360"
 170:    * height="200" alt="[An illustration that shows which pixels
 171:    * get painted in what color]" />
 172:    *
 173:    * @param g the graphics into which the rectangle is drawn.
 174:    * @param x the x coordinate of the rectangle.
 175:    * @param y the y coordinate of the rectangle.
 176:    * @param width the width of the rectangle in pixels.
 177:    * @param height the height of the rectangle in pixels.
 178:    *
 179:    * @param shadow the color that will be used for painting the outer
 180:    *        side of the top and left edges, and for the inner side of
 181:    *        the bottom and right ones.
 182:    *
 183:    * @param highlight the color that will be used for painting the
 184:    *        inner side of the top and left edges, and for the outer
 185:    *        side of the bottom and right ones.
 186:    *
 187:    * @see #getGrooveInsets()
 188:    * @see javax.swing.border.EtchedBorder
 189:    */
 190:   public static void drawGroove(Graphics g,
 191:                                 int x, int y, int width, int height,
 192:                                 Color shadow, Color highlight)
 193:   {
 194:     /* To understand this, it might be helpful to look at the image
 195:      * "BasicGraphicsUtils-2.png" that is included with the JavaDoc,
 196:      * and to compare it with "BasicGraphicsUtils-1.png" which shows
 197:      * the pixels painted by drawEtchedRect.  These image files are
 198:      * located in the "doc-files" subdirectory.
 199:      */
 200:     drawEtchedRect(g, x, y, width, height,
 201:                    /* outer topLeft */     shadow,
 202:                    /* inner topLeft */     highlight,
 203:                    /* inner bottomRight */ shadow,
 204:                    /* outer bottomRight */ highlight);
 205:   }
 206: 
 207: 
 208:   /**
 209:    * Determines the width of the border that gets painted by
 210:    * {@link #drawGroove}.
 211:    *
 212:    * @return an <code>Insets</code> object whose <code>top</code>,
 213:    *         <code>left</code>, <code>bottom</code> and
 214:    *         <code>right</code> field contain the border width at the
 215:    *         respective edge in pixels.
 216:    */
 217:   public static Insets getGrooveInsets()
 218:   {
 219:     return new Insets(2, 2, 2, 2);
 220:   }
 221:   
 222: 
 223:   /**
 224:    * Draws a border that is suitable for buttons of the Basic look and
 225:    * feel.
 226:    *
 227:    * <p><img src="doc-files/BasicGraphicsUtils-3.png" width="500"
 228:    * height="300" alt="[An illustration that shows which pixels
 229:    * get painted in what color]" />
 230:    *
 231:    * @param g the graphics into which the rectangle is drawn.
 232:    * @param x the x coordinate of the rectangle.
 233:    * @param y the y coordinate of the rectangle.
 234:    * @param width the width of the rectangle in pixels.
 235:    * @param height the height of the rectangle in pixels.
 236:    *
 237:    * @param isPressed <code>true</code> to draw the button border
 238:    *        with a pressed-in appearance; <code>false</code> for
 239:    *        normal (unpressed) appearance.
 240:    *
 241:    * @param isDefault <code>true</code> to draw the border with
 242:    *        the appearance it has when hitting the enter key in a
 243:    *        dialog will simulate a click to this button;
 244:    *        <code>false</code> for normal appearance.
 245:    *
 246:    * @param shadow the shadow color.
 247:    * @param darkShadow a darker variant of the shadow color.
 248:    * @param highlight the highlight color.
 249:    * @param lightHighlight a brighter variant of the highlight  color.
 250:    */
 251:   public static void drawBezel(Graphics g,
 252:                                int x, int y, int width, int height,
 253:                                boolean isPressed, boolean isDefault,
 254:                                Color shadow, Color darkShadow,
 255:                                Color highlight, Color lightHighlight)
 256:   {
 257:     Color oldColor = g.getColor();
 258: 
 259:     /* To understand this, it might be helpful to look at the image
 260:      * "BasicGraphicsUtils-3.png" that is included with the JavaDoc,
 261:      * and to compare it with "BasicGraphicsUtils-1.png" which shows
 262:      * the pixels painted by drawEtchedRect.  These image files are
 263:      * located in the "doc-files" subdirectory.
 264:      */
 265:     try
 266:     {
 267:       if ((isPressed == false) && (isDefault == false))
 268:       {
 269:         drawEtchedRect(g, x, y, width, height,
 270:                        lightHighlight, highlight,
 271:                        shadow, darkShadow);
 272:       }
 273: 
 274:       if ((isPressed == true) && (isDefault == false))
 275:       {
 276:         g.setColor(shadow);
 277:         g.drawRect(x + 1, y + 1, width - 2, height - 2);
 278:       }
 279: 
 280:       if ((isPressed == false) && (isDefault == true))
 281:       {
 282:         g.setColor(darkShadow);
 283:         g.drawRect(x, y, width - 1, height - 1);
 284:         drawEtchedRect(g, x + 1, y + 1, width - 2, height - 2,
 285:                        lightHighlight, highlight,
 286:                        shadow, darkShadow);
 287:       }
 288: 
 289:       if ((isPressed == true) && (isDefault == true))
 290:       {
 291:         g.setColor(darkShadow);
 292:         g.drawRect(x, y, width - 1, height - 1);
 293:         g.setColor(shadow);
 294:         g.drawRect(x + 1, y + 1, width - 3, height - 3);
 295:       }
 296:     }
 297:     finally
 298:     {
 299:       g.setColor(oldColor);
 300:     }
 301:   }
 302:   
 303:   
 304:   /**
 305:    * Draws a rectangle that appears lowered into the surface, given
 306:    * four colors that are used for drawing.
 307:    *
 308:    * <p><img src="doc-files/BasicGraphicsUtils-4.png" width="360"
 309:    * height="200" alt="[An illustration that shows which pixels
 310:    * get painted in what color]" />
 311:    *
 312:    * <p><strong>Compatibility with the Sun reference
 313:    * implementation:</strong> The Sun reference implementation seems
 314:    * to ignore the <code>x</code> and <code>y</code> arguments, at
 315:    * least in JDK 1.3.1 and 1.4.1_01.  The method always draws the
 316:    * rectangular area at location (0, 0). A bug report has been filed
 317:    * with Sun; its &#x201c;bug ID&#x201d; is 4880003.  The GNU Classpath
 318:    * implementation behaves correctly, thus not replicating this bug.
 319:    *
 320:    * @param g the graphics into which the rectangle is drawn.
 321:    * @param x the x coordinate of the rectangle.
 322:    * @param y the y coordinate of the rectangle.
 323:    * @param width the width of the rectangle in pixels.
 324:    * @param height the height of the rectangle in pixels.
 325:    *
 326:    * @param shadow the color that will be used for painting
 327:    *        the inner side of the top and left edges.
 328:    *
 329:    * @param darkShadow the color that will be used for painting
 330:    *        the outer side of the top and left edges.
 331:    *
 332:    * @param highlight the color that will be used for painting
 333:    *        the inner side of the bottom and right edges.
 334:    *
 335:    * @param lightHighlight the color that will be used for painting
 336:    *        the outer side of the bottom and right edges.
 337:    */
 338:   public static void drawLoweredBezel(Graphics g,
 339:                                       int x, int y, int width, int height,
 340:                                       Color shadow, Color darkShadow,
 341:                                       Color highlight, Color lightHighlight)
 342:   {
 343:     /* Like drawEtchedRect, but swapping darkShadow and shadow.
 344:      *
 345:      * To understand this, it might be helpful to look at the image
 346:      * "BasicGraphicsUtils-4.png" that is included with the JavaDoc,
 347:      * and to compare it with "BasicGraphicsUtils-1.png" which shows
 348:      * the pixels painted by drawEtchedRect.  These image files are
 349:      * located in the "doc-files" subdirectory.
 350:      */
 351:     drawEtchedRect(g, x, y, width, height,
 352:                    darkShadow, shadow,
 353:                    highlight, lightHighlight);
 354:   }
 355:   
 356:   
 357:   /**
 358:    * Draws a String at the given location, underlining the first
 359:    * occurence of a specified character. The algorithm for determining
 360:    * the underlined position is not sensitive to case. If the
 361:    * character is not part of <code>text</code>, the text will be
 362:    * drawn without underlining. Drawing is performed in the current
 363:    * color and font of <code>g</code>.
 364:    *
 365:    * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
 366:    * height="100" alt="[An illustration showing how to use the
 367:    * method]" />
 368:    *
 369:    * @param g the graphics into which the String is drawn.
 370:    *
 371:    * @param text the String to draw.
 372:    *
 373:    * @param underlinedChar the character whose first occurence in
 374:    *        <code>text</code> will be underlined. It is not clear
 375:    *        why the API specification declares this argument to be
 376:    *        of type <code>int</code> instead of <code>char</code>.
 377:    *        While this would allow to pass Unicode characters outside
 378:    *        Basic Multilingual Plane 0 (U+0000 .. U+FFFE), at least
 379:    *        the GNU Classpath implementation does not underline
 380:    *        anything if <code>underlinedChar</code> is outside
 381:    *        the range of <code>char</code>.
 382:    *        
 383:    * @param x the x coordinate of the text, as it would be passed to
 384:    *        {@link java.awt.Graphics#drawString(java.lang.String,
 385:    *        int, int)}.
 386:    *
 387:    * @param y the y coordinate of the text, as it would be passed to
 388:    *        {@link java.awt.Graphics#drawString(java.lang.String,
 389:    *        int, int)}.
 390:    */
 391:   public static void drawString(Graphics g, String text,
 392:                                 int underlinedChar, int x, int y)
 393:   {
 394:     int index = -1;
 395: 
 396:     /* It is intentional that lower case is used. In some languages,
 397:      * the set of lowercase characters is larger than the set of
 398:      * uppercase ones. Therefore, it is good practice to use lowercase
 399:      * for such comparisons (which really means that the author of this
 400:      * code can vaguely remember having read some Unicode techreport
 401:      * with this recommendation, but is too lazy to look for the URL).
 402:      */
 403:     if ((underlinedChar >= 0) || (underlinedChar <= 0xffff))
 404:       index = text.toLowerCase().indexOf(
 405:         Character.toLowerCase((char) underlinedChar));
 406: 
 407:     drawStringUnderlineCharAt(g, text, index, x, y);
 408:   }
 409: 
 410: 
 411:   /**
 412:    * Draws a String at the given location, underlining the character
 413:    * at the specified index. Drawing is performed in the current color
 414:    * and font of <code>g</code>.
 415:    *
 416:    * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
 417:    * height="100" alt="[An illustration showing how to use the
 418:    * method]" />
 419:    *
 420:    * @param g the graphics into which the String is drawn.
 421:    *
 422:    * @param text the String to draw.
 423:    *
 424:    * @param underlinedIndex the index of the underlined character in
 425:    *        <code>text</code>.  If <code>underlinedIndex</code> falls
 426:    *        outside the range <code>[0, text.length() - 1]</code>, the
 427:    *        text will be drawn without underlining anything.
 428:    *        
 429:    * @param x the x coordinate of the text, as it would be passed to
 430:    *        {@link java.awt.Graphics#drawString(java.lang.String,
 431:    *        int, int)}.
 432:    *
 433:    * @param y the y coordinate of the text, as it would be passed to
 434:    *        {@link java.awt.Graphics#drawString(java.lang.String,
 435:    *        int, int)}.
 436:    *
 437:    * @since 1.4
 438:    */
 439:   public static void drawStringUnderlineCharAt(Graphics g, String text,
 440:                                                int underlinedIndex,
 441:                                                int x, int y)
 442:   {
 443:     Graphics2D g2;
 444:     Rectangle2D.Double underline;
 445:     FontRenderContext frc;
 446:     FontMetrics fmet;
 447:     LineMetrics lineMetrics;
 448:     Font font;
 449:     TextLayout layout;
 450:     double underlineX1, underlineX2;
 451:     boolean drawUnderline;
 452:     int textLength;
 453: 
 454:     textLength = text.length();
 455:     if (textLength == 0)
 456:       return;
 457: 
 458:     drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength);
 459: 
 460:     // FIXME: unfortunately pango and cairo can't agree on metrics
 461:     // so for the time being we continue to *not* use TextLayouts.
 462:     if (true || !(g instanceof Graphics2D))
 463:     {
 464:       /* Fall-back. This is likely to produce garbage for any text
 465:        * containing right-to-left (Hebrew or Arabic) characters, even
 466:        * if the underlined character is left-to-right.
 467:        */
 468:       g.drawString(text, x, y);
 469:       if (drawUnderline)
 470:       {
 471:         fmet = g.getFontMetrics();
 472:         g.fillRect(
 473:           /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)),
 474:           /* y */ y + fmet.getDescent() - 1,
 475:           /* width */ fmet.charWidth(text.charAt(underlinedIndex)),
 476:           /* height */ 1);
 477:       }
 478: 
 479:       return;
 480:     }
 481: 
 482:     g2 = (Graphics2D) g;
 483:     font = g2.getFont();
 484:     frc = g2.getFontRenderContext();
 485:     lineMetrics = font.getLineMetrics(text, frc);
 486:     layout = new TextLayout(text, font, frc);
 487: 
 488:     /* Draw the text. */
 489:     layout.draw(g2, x, y);
 490:     if (!drawUnderline)
 491:       return;
 492: 
 493:     underlineX1 = x + layout.getLogicalHighlightShape(
 494:      underlinedIndex, underlinedIndex).getBounds2D().getX();
 495:     underlineX2 = x + layout.getLogicalHighlightShape(
 496:      underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX();
 497: 
 498:     underline = new Rectangle2D.Double();
 499:     if (underlineX1 < underlineX2)
 500:     {
 501:       underline.x = underlineX1;
 502:       underline.width = underlineX2 - underlineX1;
 503:     }
 504:     else
 505:     {
 506:       underline.x = underlineX2;
 507:       underline.width = underlineX1 - underlineX2;
 508:     }
 509: 
 510:     
 511:     underline.height = lineMetrics.getUnderlineThickness();
 512:     underline.y = lineMetrics.getUnderlineOffset();
 513:     if (underline.y == 0)
 514:     {
 515:       /* Some fonts do not specify an underline offset, although they
 516:        * actually should do so. In that case, the result of calling
 517:        * lineMetrics.getUnderlineOffset() will be zero. Since it would
 518:        * look very ugly if the underline was be positioned immediately
 519:        * below the baseline, we check for this and move the underline
 520:        * below the descent, as shown in the following ASCII picture:
 521:        *
 522:        *   #####       ##### #
 523:        *  #     #     #     #
 524:        *  #     #     #     #
 525:        *  #     #     #     #
 526:        *   #####       ######        ---- baseline (0)
 527:        *                    #
 528:        *                    #
 529:        * ------------------###----------- lineMetrics.getDescent()
 530:        */
 531:       underline.y = lineMetrics.getDescent();
 532:     }
 533: 
 534:     underline.y += y;
 535:     g2.fill(underline);
 536:   }
 537: 
 538: 
 539:   /**
 540:    * Draws a rectangle, simulating a dotted stroke by painting only
 541:    * every second pixel along the one-pixel thick edge. The color of
 542:    * those pixels is the current color of the Graphics <code>g</code>.
 543:    * Any other pixels are left unchanged.
 544:    *
 545:    * <p><img src="doc-files/BasicGraphicsUtils-7.png" width="360"
 546:    * height="200" alt="[An illustration that shows which pixels
 547:    * get painted]" />
 548:    *
 549:    * @param g the graphics into which the rectangle is drawn.
 550:    * @param x the x coordinate of the rectangle.
 551:    * @param y the y coordinate of the rectangle.
 552:    * @param width the width of the rectangle in pixels.
 553:    * @param height the height of the rectangle in pixels.
 554:    */
 555:   public static void drawDashedRect(Graphics g,
 556:                                     int x, int y, int width, int height)
 557:   {
 558:     int right = x + width - 1;
 559:     int bottom = y + height - 1;
 560: 
 561:     /* Draw the top and bottom edge of the dotted rectangle. */
 562:     for (int i = x; i <= right; i += 2)
 563:     {
 564:       g.drawLine(i, y, i, y);
 565:       g.drawLine(i, bottom, i, bottom);
 566:     }
 567: 
 568:     /* Draw the left and right edge of the dotted rectangle. */
 569:     for (int i = y; i <= bottom; i += 2)
 570:     {
 571:       g.drawLine(x, i, x, i);
 572:       g.drawLine(right, i, right, i);
 573:     }
 574:   }
 575: 
 576: 
 577:   /**
 578:    * Determines the preferred width and height of an AbstractButton,
 579:    * given the gap between the button&#x2019;s text and icon.
 580:    *
 581:    * @param b the button whose preferred size is determined.
 582:    *
 583:    * @param textIconGap the gap between the button&#x2019;s text and
 584:    *        icon.
 585:    *
 586:    * @return a <code>Dimension</code> object whose <code>width</code>
 587:    *         and <code>height</code> fields indicate the preferred
 588:    *         extent in pixels.
 589:    *
 590:    * @see javax.swing.SwingUtilities#layoutCompoundLabel(JComponent, 
 591:    *      FontMetrics, String, Icon, int, int, int, int, Rectangle, Rectangle, 
 592:    *      Rectangle, int)
 593:    */
 594:   public static Dimension getPreferredButtonSize(AbstractButton b,
 595:                                                  int textIconGap)
 596:   {
 597:     Rectangle contentRect;
 598:     Rectangle viewRect;
 599:     Rectangle iconRect = new Rectangle();
 600:     Rectangle textRect = new Rectangle();
 601:     Insets insets = b.getInsets();
 602:     
 603:     viewRect = new Rectangle();
 604: 
 605:      /* java.awt.Toolkit.getFontMetrics is deprecated. However, it
 606:      * seems not obvious how to get to the correct FontMetrics object
 607:      * otherwise. The real problem probably is that the method
 608:      * javax.swing.SwingUtilities.layoutCompundLabel should take a
 609:      * LineMetrics, not a FontMetrics argument. But fixing this that
 610:      * would change the public API.
 611:      */
 612:    SwingUtilities.layoutCompoundLabel(
 613:       b, // for the component orientation
 614:       b.getToolkit().getFontMetrics(b.getFont()), // see comment above
 615:       b.getText(),
 616:       b.getIcon(),
 617:       b.getVerticalAlignment(), 
 618:       b.getHorizontalAlignment(),
 619:       b.getVerticalTextPosition(),
 620:       b.getHorizontalTextPosition(),
 621:       viewRect, iconRect, textRect,
 622:       textIconGap);
 623: 
 624:     /*  +------------------------+       +------------------------+
 625:      *  |                        |       |                        |
 626:      *  | ICON                   |       | CONTENTCONTENTCONTENT  |
 627:      *  |          TEXTTEXTTEXT  |  -->  | CONTENTCONTENTCONTENT  |
 628:      *  |          TEXTTEXTTEXT  |       | CONTENTCONTENTCONTENT  |
 629:      *  +------------------------+       +------------------------+
 630:      */
 631: 
 632:     contentRect = textRect.union(iconRect);
 633:     
 634:     return new Dimension(insets.left
 635:              + contentRect.width 
 636:              + insets.right + b.getHorizontalAlignment(),
 637:                          insets.top
 638:              + contentRect.height 
 639:              + insets.bottom);
 640:   }
 641: }