1 /*
   2  * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing.plaf.synth;
  26 
  27 import sun.swing.SwingUtilities2;
  28 import sun.swing.MenuItemLayoutHelper;
  29 
  30 import java.awt.*;
  31 import javax.swing.*;
  32 import javax.swing.plaf.basic.BasicHTML;
  33 import javax.swing.text.*;
  34 import sun.swing.plaf.synth.*;
  35 
  36 /**
  37  * Wrapper for primitive graphics calls.
  38  *
  39  * @since 1.5
  40  * @author Scott Violet
  41  */
  42 public class SynthGraphicsUtils {
  43     // These are used in the text painting code to avoid allocating a bunch of
  44     // garbage.
  45     private Rectangle paintIconR = new Rectangle();
  46     private Rectangle paintTextR = new Rectangle();
  47     private Rectangle paintViewR = new Rectangle();
  48     private Insets paintInsets = new Insets(0, 0, 0, 0);
  49 
  50     // These Rectangles/Insets are used in the text size calculation to avoid a
  51     // a bunch of garbage.
  52     private Rectangle iconR = new Rectangle();
  53     private Rectangle textR = new Rectangle();
  54     private Rectangle viewR = new Rectangle();
  55     private Insets viewSizingInsets = new Insets(0, 0, 0, 0);
  56 
  57     /**
  58      * Creates a <code>SynthGraphicsUtils</code>.
  59      */
  60     public SynthGraphicsUtils() {
  61     }
  62 
  63     /**
  64      * Draws a line between the two end points.
  65      *
  66      * @param context Identifies hosting region.
  67      * @param paintKey Identifies the portion of the component being asked
  68      *                 to paint, may be null.
  69      * @param g Graphics object to paint to
  70      * @param x1 x origin
  71      * @param y1 y origin
  72      * @param x2 x destination
  73      * @param y2 y destination
  74      */
  75     public void drawLine(SynthContext context, Object paintKey,
  76                          Graphics g, int x1, int y1, int x2, int y2) {
  77         g.drawLine(x1, y1, x2, y2);
  78     }
  79 
  80     /**
  81      * Draws a line between the two end points.
  82      * <p>This implementation supports only one line style key,
  83      * <code>"dashed"</code>. The <code>"dashed"</code> line style is applied
  84      * only to vertical and horizontal lines.
  85      * <p>Specifying <code>null</code> or any key different from
  86      * <code>"dashed"</code> will draw solid lines.
  87      *
  88      * @param context identifies hosting region
  89      * @param paintKey identifies the portion of the component being asked
  90      *                 to paint, may be null
  91      * @param g Graphics object to paint to
  92      * @param x1 x origin
  93      * @param y1 y origin
  94      * @param x2 x destination
  95      * @param y2 y destination
  96      * @param styleKey identifies the requested style of the line (e.g. "dashed")
  97      * @since 1.6
  98      */
  99     public void drawLine(SynthContext context, Object paintKey,
 100                          Graphics g, int x1, int y1, int x2, int y2,
 101                          Object styleKey) {
 102         if ("dashed".equals(styleKey)) {
 103             // draw vertical line
 104             if (x1 == x2) {
 105                 y1 += (y1 % 2);
 106 
 107                 for (int y = y1; y <= y2; y+=2) {
 108                     g.drawLine(x1, y, x2, y);
 109                 }
 110             // draw horizontal line
 111             } else if (y1 == y2) {
 112                 x1 += (x1 % 2);
 113 
 114                 for (int x = x1; x <= x2; x+=2) {
 115                     g.drawLine(x, y1, x, y2);
 116                 }
 117             // oblique lines are not supported
 118             }
 119         } else {
 120             drawLine(context, paintKey, g, x1, y1, x2, y2);
 121         }
 122     }
 123 
 124     /**
 125      * Lays out text and an icon returning, by reference, the location to
 126      * place the icon and text.
 127      *
 128      * @param ss SynthContext
 129      * @param fm FontMetrics for the Font to use, this may be ignored
 130      * @param text Text to layout
 131      * @param icon Icon to layout
 132      * @param hAlign horizontal alignment
 133      * @param vAlign vertical alignment
 134      * @param hTextPosition horizontal text position
 135      * @param vTextPosition vertical text position
 136      * @param viewR Rectangle to layout text and icon in.
 137      * @param iconR Rectangle to place icon bounds in
 138      * @param textR Rectangle to place text in
 139      * @param iconTextGap gap between icon and text
 140      *
 141      * @return by reference, the location to
 142      * place the icon and text.
 143      */
 144     public String layoutText(SynthContext ss, FontMetrics fm,
 145                          String text, Icon icon, int hAlign,
 146                          int vAlign, int hTextPosition,
 147                          int vTextPosition, Rectangle viewR,
 148                          Rectangle iconR, Rectangle textR, int iconTextGap) {
 149         if (icon instanceof SynthIcon) {
 150             SynthIconWrapper wrapper = SynthIconWrapper.get((SynthIcon)icon,
 151                                                             ss);
 152             String formattedText = SwingUtilities.layoutCompoundLabel(
 153                       ss.getComponent(), fm, text, wrapper, vAlign, hAlign,
 154                       vTextPosition, hTextPosition, viewR, iconR, textR,
 155                       iconTextGap);
 156             SynthIconWrapper.release(wrapper);
 157             return formattedText;
 158         }
 159         return SwingUtilities.layoutCompoundLabel(
 160                       ss.getComponent(), fm, text, icon, vAlign, hAlign,
 161                       vTextPosition, hTextPosition, viewR, iconR, textR,
 162                       iconTextGap);
 163     }
 164 
 165     /**
 166      * Returns the size of the passed in string.
 167      *
 168      * @param ss SynthContext
 169      * @param font Font to use
 170      * @param metrics FontMetrics, may be ignored
 171      * @param text Text to get size of.
 172      *
 173      * @return the size of the passed in string.
 174      */
 175     public int computeStringWidth(SynthContext ss, Font font,
 176                                   FontMetrics metrics, String text) {
 177         return SwingUtilities2.stringWidth(ss.getComponent(), metrics,
 178                                           text);
 179     }
 180 
 181     /**
 182      * Returns the minimum size needed to properly render an icon and text.
 183      *
 184      * @param ss SynthContext
 185      * @param font Font to use
 186      * @param text Text to layout
 187      * @param icon Icon to layout
 188      * @param hAlign horizontal alignment
 189      * @param vAlign vertical alignment
 190      * @param hTextPosition horizontal text position
 191      * @param vTextPosition vertical text position
 192      * @param iconTextGap gap between icon and text
 193      * @param mnemonicIndex Index into text to render the mnemonic at, -1
 194      *        indicates no mnemonic.
 195      *
 196      * @return the minimum size needed to properly render an icon and text.
 197      */
 198     public Dimension getMinimumSize(SynthContext ss, Font font, String text,
 199                       Icon icon, int hAlign, int vAlign, int hTextPosition,
 200                       int vTextPosition, int iconTextGap, int mnemonicIndex) {
 201         JComponent c = ss.getComponent();
 202         Dimension size = getPreferredSize(ss, font, text, icon, hAlign,
 203                                           vAlign, hTextPosition, vTextPosition,
 204                                           iconTextGap, mnemonicIndex);
 205         View v = (View) c.getClientProperty(BasicHTML.propertyKey);
 206 
 207         if (v != null) {
 208             size.width -= v.getPreferredSpan(View.X_AXIS) -
 209                           v.getMinimumSpan(View.X_AXIS);
 210         }
 211         return size;
 212     }
 213 
 214     /**
 215      * Returns the maximum size needed to properly render an icon and text.
 216      *
 217      * @param ss SynthContext
 218      * @param font Font to use
 219      * @param text Text to layout
 220      * @param icon Icon to layout
 221      * @param hAlign horizontal alignment
 222      * @param vAlign vertical alignment
 223      * @param hTextPosition horizontal text position
 224      * @param vTextPosition vertical text position
 225      * @param iconTextGap gap between icon and text
 226      * @param mnemonicIndex Index into text to render the mnemonic at, -1
 227      *        indicates no mnemonic.
 228      *
 229      * @return the maximum size needed to properly render an icon and text.
 230      */
 231     public Dimension getMaximumSize(SynthContext ss, Font font, String text,
 232                       Icon icon, int hAlign, int vAlign, int hTextPosition,
 233                       int vTextPosition, int iconTextGap, int mnemonicIndex) {
 234         JComponent c = ss.getComponent();
 235         Dimension size = getPreferredSize(ss, font, text, icon, hAlign,
 236                                           vAlign, hTextPosition, vTextPosition,
 237                                           iconTextGap, mnemonicIndex);
 238         View v = (View) c.getClientProperty(BasicHTML.propertyKey);
 239 
 240         if (v != null) {
 241             size.width += v.getMaximumSpan(View.X_AXIS) -
 242                           v.getPreferredSpan(View.X_AXIS);
 243         }
 244         return size;
 245     }
 246 
 247     /**
 248      * Returns the maximum height of the Font from the passed in
 249      * SynthContext.
 250      *
 251      * @param context SynthContext used to determine font.
 252      * @return maximum height of the characters for the font from the passed
 253      *         in context.
 254      */
 255     public int getMaximumCharHeight(SynthContext context) {
 256         FontMetrics fm = context.getComponent().getFontMetrics(
 257             context.getStyle().getFont(context));
 258         return (fm.getAscent() + fm.getDescent());
 259     }
 260 
 261     /**
 262      * Returns the preferred size needed to properly render an icon and text.
 263      *
 264      * @param ss SynthContext
 265      * @param font Font to use
 266      * @param text Text to layout
 267      * @param icon Icon to layout
 268      * @param hAlign horizontal alignment
 269      * @param vAlign vertical alignment
 270      * @param hTextPosition horizontal text position
 271      * @param vTextPosition vertical text position
 272      * @param iconTextGap gap between icon and text
 273      * @param mnemonicIndex Index into text to render the mnemonic at, -1
 274      *        indicates no mnemonic.
 275      *
 276      * @return the preferred size needed to properly render an icon and text.
 277      */
 278     public Dimension getPreferredSize(SynthContext ss, Font font, String text,
 279                       Icon icon, int hAlign, int vAlign, int hTextPosition,
 280                       int vTextPosition, int iconTextGap, int mnemonicIndex) {
 281         JComponent c = ss.getComponent();
 282         Insets insets = c.getInsets(viewSizingInsets);
 283         int dx = insets.left + insets.right;
 284         int dy = insets.top + insets.bottom;
 285 
 286         if (icon == null && (text == null || font == null)) {
 287             return new Dimension(dx, dy);
 288         }
 289         else if ((text == null) || ((icon != null) && (font == null))) {
 290             return new Dimension(SynthIcon.getIconWidth(icon, ss) + dx,
 291                                  SynthIcon.getIconHeight(icon, ss) + dy);
 292         }
 293         else {
 294             FontMetrics fm = c.getFontMetrics(font);
 295 
 296             iconR.x = iconR.y = iconR.width = iconR.height = 0;
 297             textR.x = textR.y = textR.width = textR.height = 0;
 298             viewR.x = dx;
 299             viewR.y = dy;
 300             viewR.width = viewR.height = Short.MAX_VALUE;
 301 
 302             layoutText(ss, fm, text, icon, hAlign, vAlign,
 303                    hTextPosition, vTextPosition, viewR, iconR, textR,
 304                    iconTextGap);
 305             int x1 = Math.min(iconR.x, textR.x);
 306             int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width);
 307             int y1 = Math.min(iconR.y, textR.y);
 308             int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height);
 309             Dimension rv = new Dimension(x2 - x1, y2 - y1);
 310 
 311             rv.width += dx;
 312             rv.height += dy;
 313             return rv;
 314         }
 315     }
 316 
 317     /**
 318      * Paints text at the specified location. This will not attempt to
 319      * render the text as html nor will it offset by the insets of the
 320      * component.
 321      *
 322      * @param ss SynthContext
 323      * @param g Graphics used to render string in.
 324      * @param text Text to render
 325      * @param bounds Bounds of the text to be drawn.
 326      * @param mnemonicIndex Index to draw string at.
 327      */
 328     public void paintText(SynthContext ss, Graphics g, String text,
 329                           Rectangle bounds, int mnemonicIndex) {
 330         paintText(ss, g, text, bounds.x, bounds.y, mnemonicIndex);
 331     }
 332 
 333     /**
 334      * Paints text at the specified location. This will not attempt to
 335      * render the text as html nor will it offset by the insets of the
 336      * component.
 337      *
 338      * @param ss SynthContext
 339      * @param g Graphics used to render string in.
 340      * @param text Text to render
 341      * @param x X location to draw text at.
 342      * @param y Upper left corner to draw text at.
 343      * @param mnemonicIndex Index to draw string at.
 344      */
 345     public void paintText(SynthContext ss, Graphics g, String text,
 346                           int x, int y, int mnemonicIndex) {
 347         if (text != null) {
 348             JComponent c = ss.getComponent();
 349             FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
 350             y += fm.getAscent();
 351             SwingUtilities2.drawStringUnderlineCharAt(c, g, text,
 352                                                       mnemonicIndex, x, y);
 353         }
 354     }
 355 
 356     /**
 357      * Paints an icon and text. This will render the text as html, if
 358      * necessary, and offset the location by the insets of the component.
 359      *
 360      * @param ss SynthContext
 361      * @param g Graphics to render string and icon into
 362      * @param text Text to layout
 363      * @param icon Icon to layout
 364      * @param hAlign horizontal alignment
 365      * @param vAlign vertical alignment
 366      * @param hTextPosition horizontal text position
 367      * @param vTextPosition vertical text position
 368      * @param iconTextGap gap between icon and text
 369      * @param mnemonicIndex Index into text to render the mnemonic at, -1
 370      *        indicates no mnemonic.
 371      * @param textOffset Amount to offset the text when painting
 372      */
 373     public void paintText(SynthContext ss, Graphics g, String text,
 374                       Icon icon, int hAlign, int vAlign, int hTextPosition,
 375                       int vTextPosition, int iconTextGap, int mnemonicIndex,
 376                       int textOffset) {
 377         if ((icon == null) && (text == null)) {
 378             return;
 379         }
 380         JComponent c = ss.getComponent();
 381         FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
 382         Insets insets = SynthLookAndFeel.getPaintingInsets(ss, paintInsets);
 383 
 384         paintViewR.x = insets.left;
 385         paintViewR.y = insets.top;
 386         paintViewR.width = c.getWidth() - (insets.left + insets.right);
 387         paintViewR.height = c.getHeight() - (insets.top + insets.bottom);
 388 
 389         paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
 390         paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
 391 
 392         String clippedText =
 393             layoutText(ss, fm, text, icon, hAlign, vAlign,
 394                    hTextPosition, vTextPosition, paintViewR, paintIconR,
 395                    paintTextR, iconTextGap);
 396 
 397         if (icon != null) {
 398             Color color = g.getColor();
 399 
 400             if (ss.getStyle().getBoolean(ss, "TableHeader.alignSorterArrow", false) &&
 401                 "TableHeader.renderer".equals(c.getName())) {
 402                 paintIconR.x = paintViewR.width - paintIconR.width;
 403             } else {
 404                 paintIconR.x += textOffset;
 405             }
 406             paintIconR.y += textOffset;
 407             SynthIcon.paintIcon(icon, ss, g, paintIconR.x, paintIconR.y,
 408                                 paintIconR.width, paintIconR.height);
 409             g.setColor(color);
 410         }
 411 
 412         if (text != null) {
 413             View v = (View) c.getClientProperty(BasicHTML.propertyKey);
 414 
 415             if (v != null) {
 416                 v.paint(g, paintTextR);
 417             } else {
 418                 paintTextR.x += textOffset;
 419                 paintTextR.y += textOffset;
 420 
 421                 paintText(ss, g, clippedText, paintTextR, mnemonicIndex);
 422             }
 423         }
 424     }
 425 
 426 
 427      /**
 428       * A quick note about how preferred sizes are calculated... Generally
 429       * speaking, SynthPopupMenuUI will run through the list of its children
 430       * (from top to bottom) and ask each for its preferred size.  Each menu
 431       * item will add up the max width of each element (icons, text,
 432       * accelerator spacing, accelerator text or arrow icon) encountered thus
 433       * far, so by the time all menu items have been calculated, we will
 434       * know the maximum (preferred) menu item size for that popup menu.
 435       * Later when it comes time to paint each menu item, we can use those
 436       * same accumulated max element sizes in order to layout the item.
 437       */
 438     static Dimension getPreferredMenuItemSize(SynthContext context,
 439            SynthContext accContext, JComponent c,
 440            Icon checkIcon, Icon arrowIcon, int defaultTextIconGap,
 441            String acceleratorDelimiter, boolean useCheckAndArrow,
 442            String propertyPrefix) {
 443 
 444          JMenuItem mi = (JMenuItem) c;
 445          SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper(
 446                  context, accContext, mi, checkIcon, arrowIcon,
 447                  MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap,
 448                  acceleratorDelimiter, SynthLookAndFeel.isLeftToRight(mi),
 449                  useCheckAndArrow, propertyPrefix);
 450 
 451          Dimension result = new Dimension();
 452 
 453          // Calculate the result width
 454          int gap = lh.getGap();
 455          result.width = 0;
 456          MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(), gap, result);
 457          MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), gap, result);
 458          MenuItemLayoutHelper.addWidth(lh.getMaxAccOrArrowWidth(), 5 * gap, result);
 459          // The last gap is unnecessary
 460          result.width -= gap;
 461 
 462          // Calculate the result height
 463          result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(),
 464                  lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(),
 465                  lh.getArrowSize().getHeight());
 466 
 467          // Take into account menu item insets
 468          Insets insets = lh.getMenuItem().getInsets();
 469          if (insets != null) {
 470              result.width += insets.left + insets.right;
 471              result.height += insets.top + insets.bottom;
 472          }
 473 
 474          // if the width is even, bump it up one. This is critical
 475          // for the focus dash lhne to draw properly
 476          if (result.width % 2 == 0) {
 477              result.width++;
 478          }
 479 
 480          // if the height is even, bump it up one. This is critical
 481          // for the text to center properly
 482          if (result.height % 2 == 0) {
 483              result.height++;
 484          }
 485 
 486          return result;
 487      }
 488 
 489     static void applyInsets(Rectangle rect, Insets insets, boolean leftToRight) {
 490         if (insets != null) {
 491             rect.x += (leftToRight ? insets.left : insets.right);
 492             rect.y += insets.top;
 493             rect.width -= (leftToRight ? insets.right : insets.left) + rect.x;
 494             rect.height -= (insets.bottom + rect.y);
 495         }
 496     }
 497 
 498     static void paint(SynthContext context, SynthContext accContext, Graphics g,
 499                Icon checkIcon, Icon arrowIcon, String acceleratorDelimiter,
 500                int defaultTextIconGap, String propertyPrefix) {
 501         JMenuItem mi = (JMenuItem) context.getComponent();
 502         SynthStyle style = context.getStyle();
 503         g.setFont(style.getFont(context));
 504 
 505         Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight());
 506         boolean leftToRight = SynthLookAndFeel.isLeftToRight(mi);
 507         applyInsets(viewRect, mi.getInsets(), leftToRight);
 508 
 509         SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper(
 510                 context, accContext, mi, checkIcon, arrowIcon, viewRect,
 511                 defaultTextIconGap, acceleratorDelimiter, leftToRight,
 512                 MenuItemLayoutHelper.useCheckAndArrow(mi), propertyPrefix);
 513         MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem();
 514 
 515         paintMenuItem(g, lh, lr);
 516     }
 517 
 518     static void paintMenuItem(Graphics g, SynthMenuItemLayoutHelper lh,
 519                               MenuItemLayoutHelper.LayoutResult lr) {
 520         // Save original graphics font and color
 521         Font holdf = g.getFont();
 522         Color holdc = g.getColor();
 523 
 524         paintCheckIcon(g, lh, lr);
 525         paintIcon(g, lh, lr);
 526         paintText(g, lh, lr);
 527         paintAccText(g, lh, lr);
 528         paintArrowIcon(g, lh, lr);
 529 
 530         // Restore original graphics font and color
 531         g.setColor(holdc);
 532         g.setFont(holdf);
 533     }
 534 
 535     static void paintBackground(Graphics g, SynthMenuItemLayoutHelper lh) {
 536         paintBackground(lh.getContext(), g, lh.getMenuItem());
 537     }
 538 
 539     static void paintBackground(SynthContext context, Graphics g, JComponent c) {
 540         context.getPainter().paintMenuItemBackground(context, g, 0, 0,
 541                 c.getWidth(), c.getHeight());
 542     }
 543 
 544     static void paintIcon(Graphics g, SynthMenuItemLayoutHelper lh,
 545                           MenuItemLayoutHelper.LayoutResult lr) {
 546         if (lh.getIcon() != null) {
 547             Icon icon;
 548             JMenuItem mi = lh.getMenuItem();
 549             ButtonModel model = mi.getModel();
 550             if (!model.isEnabled()) {
 551                 icon = mi.getDisabledIcon();
 552             } else if (model.isPressed() && model.isArmed()) {
 553                 icon = mi.getPressedIcon();
 554                 if (icon == null) {
 555                     // Use default icon
 556                     icon = mi.getIcon();
 557                 }
 558             } else {
 559                 icon = mi.getIcon();
 560             }
 561 
 562             if (icon != null) {
 563                 Rectangle iconRect = lr.getIconRect();
 564                 SynthIcon.paintIcon(icon, lh.getContext(), g, iconRect.x,
 565                         iconRect.y, iconRect.width, iconRect.height);
 566             }
 567         }
 568     }
 569 
 570     static void paintCheckIcon(Graphics g, SynthMenuItemLayoutHelper lh,
 571                                MenuItemLayoutHelper.LayoutResult lr) {
 572         if (lh.getCheckIcon() != null) {
 573             Rectangle checkRect = lr.getCheckRect();
 574             SynthIcon.paintIcon(lh.getCheckIcon(), lh.getContext(), g,
 575                     checkRect.x, checkRect.y, checkRect.width, checkRect.height);
 576         }
 577     }
 578 
 579     static void paintAccText(Graphics g, SynthMenuItemLayoutHelper lh,
 580                              MenuItemLayoutHelper.LayoutResult lr) {
 581         String accText = lh.getAccText();
 582         if (accText != null && !accText.equals("")) {
 583             g.setColor(lh.getAccStyle().getColor(lh.getAccContext(),
 584                     ColorType.TEXT_FOREGROUND));
 585             g.setFont(lh.getAccStyle().getFont(lh.getAccContext()));
 586             lh.getAccGraphicsUtils().paintText(lh.getAccContext(), g, accText,
 587                     lr.getAccRect().x, lr.getAccRect().y, -1);
 588         }
 589     }
 590 
 591     static void paintText(Graphics g, SynthMenuItemLayoutHelper lh,
 592                           MenuItemLayoutHelper.LayoutResult lr) {
 593         if (!lh.getText().equals("")) {
 594             if (lh.getHtmlView() != null) {
 595                 // Text is HTML
 596                 lh.getHtmlView().paint(g, lr.getTextRect());
 597             } else {
 598                 // Text isn't HTML
 599                 g.setColor(lh.getStyle().getColor(
 600                         lh.getContext(), ColorType.TEXT_FOREGROUND));
 601                 g.setFont(lh.getStyle().getFont(lh.getContext()));
 602                 lh.getGraphicsUtils().paintText(lh.getContext(), g, lh.getText(),
 603                         lr.getTextRect().x, lr.getTextRect().y,
 604                         lh.getMenuItem().getDisplayedMnemonicIndex());
 605             }
 606         }
 607     }
 608 
 609     static void paintArrowIcon(Graphics g, SynthMenuItemLayoutHelper lh,
 610                                MenuItemLayoutHelper.LayoutResult lr) {
 611         if (lh.getArrowIcon() != null) {
 612             Rectangle arrowRect = lr.getArrowRect();
 613             SynthIcon.paintIcon(lh.getArrowIcon(), lh.getContext(), g,
 614                     arrowRect.x, arrowRect.y, arrowRect.width, arrowRect.height);
 615         }
 616     }
 617 
 618     /**
 619      * Wraps a SynthIcon around the Icon interface, forwarding calls to
 620      * the SynthIcon with a given SynthContext.
 621      */
 622     private static class SynthIconWrapper implements Icon {
 623         private static final java.util.List<SynthIconWrapper> CACHE = new java.util.ArrayList<SynthIconWrapper>(1);
 624 
 625         private SynthIcon synthIcon;
 626         private SynthContext context;
 627 
 628         static SynthIconWrapper get(SynthIcon icon, SynthContext context) {
 629             synchronized(CACHE) {
 630                 int size = CACHE.size();
 631                 if (size > 0) {
 632                     SynthIconWrapper wrapper = CACHE.remove(size - 1);
 633                     wrapper.reset(icon, context);
 634                     return wrapper;
 635                 }
 636             }
 637             return new SynthIconWrapper(icon, context);
 638         }
 639 
 640         static void release(SynthIconWrapper wrapper) {
 641             wrapper.reset(null, null);
 642             synchronized(CACHE) {
 643                 CACHE.add(wrapper);
 644             }
 645         }
 646 
 647         SynthIconWrapper(SynthIcon icon, SynthContext context) {
 648             reset(icon, context);
 649         }
 650 
 651         void reset(SynthIcon icon, SynthContext context) {
 652             synthIcon = icon;
 653             this.context = context;
 654         }
 655 
 656         public void paintIcon(Component c, Graphics g, int x, int y) {
 657             // This is a noop as this should only be for sizing calls.
 658         }
 659 
 660         public int getIconWidth() {
 661             return synthIcon.getIconWidth(context);
 662         }
 663 
 664         public int getIconHeight() {
 665             return synthIcon.getIconHeight(context);
 666         }
 667     }
 668 }
--- EOF ---