1 /* 2 * Copyright (c) 2002, 2008, 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 public String layoutText(SynthContext ss, FontMetrics fm, 142 String text, Icon icon, int hAlign, 143 int vAlign, int hTextPosition, 144 int vTextPosition, Rectangle viewR, 145 Rectangle iconR, Rectangle textR, int iconTextGap) { 146 if (icon instanceof SynthIcon) { 147 SynthIconWrapper wrapper = SynthIconWrapper.get((SynthIcon)icon, 148 ss); 149 String formattedText = SwingUtilities.layoutCompoundLabel( 150 ss.getComponent(), fm, text, wrapper, vAlign, hAlign, 151 vTextPosition, hTextPosition, viewR, iconR, textR, 152 iconTextGap); 153 SynthIconWrapper.release(wrapper); 154 return formattedText; 155 } 156 return SwingUtilities.layoutCompoundLabel( 157 ss.getComponent(), fm, text, icon, vAlign, hAlign, 158 vTextPosition, hTextPosition, viewR, iconR, textR, 159 iconTextGap); 160 } 161 162 /** 163 * Returns the size of the passed in string. 164 * 165 * @param ss SynthContext 166 * @param font Font to use 167 * @param metrics FontMetrics, may be ignored 168 * @param text Text to get size of. 169 */ 170 public int computeStringWidth(SynthContext ss, Font font, 171 FontMetrics metrics, String text) { 172 return SwingUtilities2.stringWidth(ss.getComponent(), metrics, 173 text); 174 } 175 176 /** 177 * Returns the minimum size needed to properly render an icon and text. 178 * 179 * @param ss SynthContext 180 * @param font Font to use 181 * @param text Text to layout 182 * @param icon Icon to layout 183 * @param hAlign horizontal alignment 184 * @param vAlign vertical alignment 185 * @param hTextPosition horizontal text position 186 * @param vTextPosition vertical text position 187 * @param iconTextGap gap between icon and text 188 * @param mnemonicIndex Index into text to render the mnemonic at, -1 189 * indicates no mnemonic. 190 */ 191 public Dimension getMinimumSize(SynthContext ss, Font font, String text, 192 Icon icon, int hAlign, int vAlign, int hTextPosition, 193 int vTextPosition, int iconTextGap, int mnemonicIndex) { 194 JComponent c = ss.getComponent(); 195 Dimension size = getPreferredSize(ss, font, text, icon, hAlign, 196 vAlign, hTextPosition, vTextPosition, 197 iconTextGap, mnemonicIndex); 198 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 199 200 if (v != null) { 201 size.width -= v.getPreferredSpan(View.X_AXIS) - 202 v.getMinimumSpan(View.X_AXIS); 203 } 204 return size; 205 } 206 207 /** 208 * Returns the maximum size needed to properly render an icon and text. 209 * 210 * @param ss SynthContext 211 * @param font Font to use 212 * @param text Text to layout 213 * @param icon Icon to layout 214 * @param hAlign horizontal alignment 215 * @param vAlign vertical alignment 216 * @param hTextPosition horizontal text position 217 * @param vTextPosition vertical text position 218 * @param iconTextGap gap between icon and text 219 * @param mnemonicIndex Index into text to render the mnemonic at, -1 220 * indicates no mnemonic. 221 */ 222 public Dimension getMaximumSize(SynthContext ss, Font font, String text, 223 Icon icon, int hAlign, int vAlign, int hTextPosition, 224 int vTextPosition, int iconTextGap, int mnemonicIndex) { 225 JComponent c = ss.getComponent(); 226 Dimension size = getPreferredSize(ss, font, text, icon, hAlign, 227 vAlign, hTextPosition, vTextPosition, 228 iconTextGap, mnemonicIndex); 229 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 230 231 if (v != null) { 232 size.width += v.getMaximumSpan(View.X_AXIS) - 233 v.getPreferredSpan(View.X_AXIS); 234 } 235 return size; 236 } 237 238 /** 239 * Returns the maximum height of the the Font from the passed in 240 * SynthContext. 241 * 242 * @param context SynthContext used to determine font. 243 * @return maximum height of the characters for the font from the passed 244 * in context. 245 */ 246 public int getMaximumCharHeight(SynthContext context) { 247 FontMetrics fm = context.getComponent().getFontMetrics( 248 context.getStyle().getFont(context)); 249 return (fm.getAscent() + fm.getDescent()); 250 } 251 252 /** 253 * Returns the preferred size needed to properly render an icon and text. 254 * 255 * @param ss SynthContext 256 * @param font Font to use 257 * @param text Text to layout 258 * @param icon Icon to layout 259 * @param hAlign horizontal alignment 260 * @param vAlign vertical alignment 261 * @param hTextPosition horizontal text position 262 * @param vTextPosition vertical text position 263 * @param iconTextGap gap between icon and text 264 * @param mnemonicIndex Index into text to render the mnemonic at, -1 265 * indicates no mnemonic. 266 */ 267 public Dimension getPreferredSize(SynthContext ss, Font font, String text, 268 Icon icon, int hAlign, int vAlign, int hTextPosition, 269 int vTextPosition, int iconTextGap, int mnemonicIndex) { 270 JComponent c = ss.getComponent(); 271 Insets insets = c.getInsets(viewSizingInsets); 272 int dx = insets.left + insets.right; 273 int dy = insets.top + insets.bottom; 274 275 if (icon == null && (text == null || font == null)) { 276 return new Dimension(dx, dy); 277 } 278 else if ((text == null) || ((icon != null) && (font == null))) { 279 return new Dimension(SynthIcon.getIconWidth(icon, ss) + dx, 280 SynthIcon.getIconHeight(icon, ss) + dy); 281 } 282 else { 283 FontMetrics fm = c.getFontMetrics(font); 284 285 iconR.x = iconR.y = iconR.width = iconR.height = 0; 286 textR.x = textR.y = textR.width = textR.height = 0; 287 viewR.x = dx; 288 viewR.y = dy; 289 viewR.width = viewR.height = Short.MAX_VALUE; 290 291 layoutText(ss, fm, text, icon, hAlign, vAlign, 292 hTextPosition, vTextPosition, viewR, iconR, textR, 293 iconTextGap); 294 int x1 = Math.min(iconR.x, textR.x); 295 int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width); 296 int y1 = Math.min(iconR.y, textR.y); 297 int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height); 298 Dimension rv = new Dimension(x2 - x1, y2 - y1); 299 300 rv.width += dx; 301 rv.height += dy; 302 return rv; 303 } 304 } 305 306 /** 307 * Paints text at the specified location. This will not attempt to 308 * render the text as html nor will it offset by the insets of the 309 * component. 310 * 311 * @param ss SynthContext 312 * @param g Graphics used to render string in. 313 * @param text Text to render 314 * @param bounds Bounds of the text to be drawn. 315 * @param mnemonicIndex Index to draw string at. 316 */ 317 public void paintText(SynthContext ss, Graphics g, String text, 318 Rectangle bounds, int mnemonicIndex) { 319 paintText(ss, g, text, bounds.x, bounds.y, mnemonicIndex); 320 } 321 322 /** 323 * Paints text at the specified location. This will not attempt to 324 * render the text as html nor will it offset by the insets of the 325 * component. 326 * 327 * @param ss SynthContext 328 * @param g Graphics used to render string in. 329 * @param text Text to render 330 * @param x X location to draw text at. 331 * @param y Upper left corner to draw text at. 332 * @param mnemonicIndex Index to draw string at. 333 */ 334 public void paintText(SynthContext ss, Graphics g, String text, 335 int x, int y, int mnemonicIndex) { 336 if (text != null) { 337 JComponent c = ss.getComponent(); 338 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g); 339 y += fm.getAscent(); 340 SwingUtilities2.drawStringUnderlineCharAt(c, g, text, 341 mnemonicIndex, x, y); 342 } 343 } 344 345 /** 346 * Paints an icon and text. This will render the text as html, if 347 * necessary, and offset the location by the insets of the component. 348 * 349 * @param ss SynthContext 350 * @param g Graphics to render string and icon into 351 * @param text Text to layout 352 * @param icon Icon to layout 353 * @param hAlign horizontal alignment 354 * @param vAlign vertical alignment 355 * @param hTextPosition horizontal text position 356 * @param vTextPosition vertical text position 357 * @param iconTextGap gap between icon and text 358 * @param mnemonicIndex Index into text to render the mnemonic at, -1 359 * indicates no mnemonic. 360 * @param textOffset Amount to offset the text when painting 361 */ 362 public void paintText(SynthContext ss, Graphics g, String text, 363 Icon icon, int hAlign, int vAlign, int hTextPosition, 364 int vTextPosition, int iconTextGap, int mnemonicIndex, 365 int textOffset) { 366 if ((icon == null) && (text == null)) { 367 return; 368 } 369 JComponent c = ss.getComponent(); 370 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g); 371 Insets insets = SynthLookAndFeel.getPaintingInsets(ss, paintInsets); 372 373 paintViewR.x = insets.left; 374 paintViewR.y = insets.top; 375 paintViewR.width = c.getWidth() - (insets.left + insets.right); 376 paintViewR.height = c.getHeight() - (insets.top + insets.bottom); 377 378 paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0; 379 paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0; 380 381 String clippedText = 382 layoutText(ss, fm, text, icon, hAlign, vAlign, 383 hTextPosition, vTextPosition, paintViewR, paintIconR, 384 paintTextR, iconTextGap); 385 386 if (icon != null) { 387 Color color = g.getColor(); 388 389 if (ss.getStyle().getBoolean(ss, "TableHeader.alignSorterArrow", false) && 390 "TableHeader.renderer".equals(c.getName())) { 391 paintIconR.x = paintViewR.width - paintIconR.width; 392 } else { 393 paintIconR.x += textOffset; 394 } 395 paintIconR.y += textOffset; 396 SynthIcon.paintIcon(icon, ss, g, paintIconR.x, paintIconR.y, 397 paintIconR.width, paintIconR.height); 398 g.setColor(color); 399 } 400 401 if (text != null) { 402 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 403 404 if (v != null) { 405 v.paint(g, paintTextR); 406 } else { 407 paintTextR.x += textOffset; 408 paintTextR.y += textOffset; 409 410 paintText(ss, g, clippedText, paintTextR, mnemonicIndex); 411 } 412 } 413 } 414 415 416 /** 417 * A quick note about how preferred sizes are calculated... Generally 418 * speaking, SynthPopupMenuUI will run through the list of its children 419 * (from top to bottom) and ask each for its preferred size. Each menu 420 * item will add up the max width of each element (icons, text, 421 * accelerator spacing, accelerator text or arrow icon) encountered thus 422 * far, so by the time all menu items have been calculated, we will 423 * know the maximum (preferred) menu item size for that popup menu. 424 * Later when it comes time to paint each menu item, we can use those 425 * same accumulated max element sizes in order to layout the item. 426 */ 427 static Dimension getPreferredMenuItemSize(SynthContext context, 428 SynthContext accContext, JComponent c, 429 Icon checkIcon, Icon arrowIcon, int defaultTextIconGap, 430 String acceleratorDelimiter, boolean useCheckAndArrow, 431 String propertyPrefix) { 432 433 JMenuItem mi = (JMenuItem) c; 434 SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper( 435 context, accContext, mi, checkIcon, arrowIcon, 436 MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap, 437 acceleratorDelimiter, SynthLookAndFeel.isLeftToRight(mi), 438 useCheckAndArrow, propertyPrefix); 439 440 Dimension result = new Dimension(); 441 442 // Calculate the result width 443 int gap = lh.getGap(); 444 result.width = 0; 445 MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(), gap, result); 446 MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), gap, result); 447 MenuItemLayoutHelper.addWidth(lh.getMaxAccOrArrowWidth(), 5 * gap, result); 448 // The last gap is unnecessary 449 result.width -= gap; 450 451 // Calculate the result height 452 result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(), 453 lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(), 454 lh.getArrowSize().getHeight()); 455 456 // Take into account menu item insets 457 Insets insets = lh.getMenuItem().getInsets(); 458 if (insets != null) { 459 result.width += insets.left + insets.right; 460 result.height += insets.top + insets.bottom; 461 } 462 463 // if the width is even, bump it up one. This is critical 464 // for the focus dash lhne to draw properly 465 if (result.width % 2 == 0) { 466 result.width++; 467 } 468 469 // if the height is even, bump it up one. This is critical 470 // for the text to center properly 471 if (result.height % 2 == 0) { 472 result.height++; 473 } 474 475 return result; 476 } 477 478 static void applyInsets(Rectangle rect, Insets insets, boolean leftToRight) { 479 if (insets != null) { 480 rect.x += (leftToRight ? insets.left : insets.right); 481 rect.y += insets.top; 482 rect.width -= (leftToRight ? insets.right : insets.left) + rect.x; 483 rect.height -= (insets.bottom + rect.y); 484 } 485 } 486 487 static void paint(SynthContext context, SynthContext accContext, Graphics g, 488 Icon checkIcon, Icon arrowIcon, String acceleratorDelimiter, 489 int defaultTextIconGap, String propertyPrefix) { 490 JMenuItem mi = (JMenuItem) context.getComponent(); 491 SynthStyle style = context.getStyle(); 492 g.setFont(style.getFont(context)); 493 494 Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); 495 boolean leftToRight = SynthLookAndFeel.isLeftToRight(mi); 496 applyInsets(viewRect, mi.getInsets(), leftToRight); 497 498 SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper( 499 context, accContext, mi, checkIcon, arrowIcon, viewRect, 500 defaultTextIconGap, acceleratorDelimiter, leftToRight, 501 MenuItemLayoutHelper.useCheckAndArrow(mi), propertyPrefix); 502 MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); 503 504 paintMenuItem(g, lh, lr); 505 } 506 507 static void paintMenuItem(Graphics g, SynthMenuItemLayoutHelper lh, 508 MenuItemLayoutHelper.LayoutResult lr) { 509 // Save original graphics font and color 510 Font holdf = g.getFont(); 511 Color holdc = g.getColor(); 512 513 paintBackground(g, lh); 514 paintCheckIcon(g, lh, lr); 515 paintIcon(g, lh, lr); 516 paintText(g, lh, lr); 517 paintAccText(g, lh, lr); 518 paintArrowIcon(g, lh, lr); 519 520 // Restore original graphics font and color 521 g.setColor(holdc); 522 g.setFont(holdf); 523 } 524 525 static void paintBackground(Graphics g, SynthMenuItemLayoutHelper lh) { 526 paintBackground(lh.getContext(), g, lh.getMenuItem()); 527 } 528 529 static void paintBackground(SynthContext context, Graphics g, JComponent c) { 530 context.getPainter().paintMenuItemBackground(context, g, 0, 0, 531 c.getWidth(), c.getHeight()); 532 } 533 534 static void paintIcon(Graphics g, SynthMenuItemLayoutHelper lh, 535 MenuItemLayoutHelper.LayoutResult lr) { 536 if (lh.getIcon() != null) { 537 Icon icon; 538 JMenuItem mi = lh.getMenuItem(); 539 ButtonModel model = mi.getModel(); 540 if (!model.isEnabled()) { 541 icon = mi.getDisabledIcon(); 542 } else if (model.isPressed() && model.isArmed()) { 543 icon = mi.getPressedIcon(); 544 if (icon == null) { 545 // Use default icon 546 icon = mi.getIcon(); 547 } 548 } else { 549 icon = mi.getIcon(); 550 } 551 552 if (icon != null) { 553 Rectangle iconRect = lr.getIconRect(); 554 SynthIcon.paintIcon(icon, lh.getContext(), g, iconRect.x, 555 iconRect.y, iconRect.width, iconRect.height); 556 } 557 } 558 } 559 560 static void paintCheckIcon(Graphics g, SynthMenuItemLayoutHelper lh, 561 MenuItemLayoutHelper.LayoutResult lr) { 562 if (lh.getCheckIcon() != null) { 563 Rectangle checkRect = lr.getCheckRect(); 564 SynthIcon.paintIcon(lh.getCheckIcon(), lh.getContext(), g, 565 checkRect.x, checkRect.y, checkRect.width, checkRect.height); 566 } 567 } 568 569 static void paintAccText(Graphics g, SynthMenuItemLayoutHelper lh, 570 MenuItemLayoutHelper.LayoutResult lr) { 571 String accText = lh.getAccText(); 572 if (accText != null && !accText.equals("")) { 573 g.setColor(lh.getAccStyle().getColor(lh.getAccContext(), 574 ColorType.TEXT_FOREGROUND)); 575 g.setFont(lh.getAccStyle().getFont(lh.getAccContext())); 576 lh.getAccGraphicsUtils().paintText(lh.getAccContext(), g, accText, 577 lr.getAccRect().x, lr.getAccRect().y, -1); 578 } 579 } 580 581 static void paintText(Graphics g, SynthMenuItemLayoutHelper lh, 582 MenuItemLayoutHelper.LayoutResult lr) { 583 if (!lh.getText().equals("")) { 584 if (lh.getHtmlView() != null) { 585 // Text is HTML 586 lh.getHtmlView().paint(g, lr.getTextRect()); 587 } else { 588 // Text isn't HTML 589 g.setColor(lh.getStyle().getColor( 590 lh.getContext(), ColorType.TEXT_FOREGROUND)); 591 g.setFont(lh.getStyle().getFont(lh.getContext())); 592 lh.getGraphicsUtils().paintText(lh.getContext(), g, lh.getText(), 593 lr.getTextRect().x, lr.getTextRect().y, 594 lh.getMenuItem().getDisplayedMnemonicIndex()); 595 } 596 } 597 } 598 599 static void paintArrowIcon(Graphics g, SynthMenuItemLayoutHelper lh, 600 MenuItemLayoutHelper.LayoutResult lr) { 601 if (lh.getArrowIcon() != null) { 602 Rectangle arrowRect = lr.getArrowRect(); 603 SynthIcon.paintIcon(lh.getArrowIcon(), lh.getContext(), g, 604 arrowRect.x, arrowRect.y, arrowRect.width, arrowRect.height); 605 } 606 } 607 608 /** 609 * Wraps a SynthIcon around the Icon interface, forwarding calls to 610 * the SynthIcon with a given SynthContext. 611 */ 612 private static class SynthIconWrapper implements Icon { 613 private static final java.util.List<SynthIconWrapper> CACHE = new java.util.ArrayList<SynthIconWrapper>(1); 614 615 private SynthIcon synthIcon; 616 private SynthContext context; 617 618 static SynthIconWrapper get(SynthIcon icon, SynthContext context) { 619 synchronized(CACHE) { 620 int size = CACHE.size(); 621 if (size > 0) { 622 SynthIconWrapper wrapper = CACHE.remove(size - 1); 623 wrapper.reset(icon, context); 624 return wrapper; 625 } 626 } 627 return new SynthIconWrapper(icon, context); 628 } 629 630 static void release(SynthIconWrapper wrapper) { 631 wrapper.reset(null, null); 632 synchronized(CACHE) { 633 CACHE.add(wrapper); 634 } 635 } 636 637 SynthIconWrapper(SynthIcon icon, SynthContext context) { 638 reset(icon, context); 639 } 640 641 void reset(SynthIcon icon, SynthContext context) { 642 synthIcon = icon; 643 this.context = context; 644 } 645 646 public void paintIcon(Component c, Graphics g, int x, int y) { 647 // This is a noop as this should only be for sizing calls. 648 } 649 650 public int getIconWidth() { 651 return synthIcon.getIconWidth(context); 652 } 653 654 public int getIconHeight() { 655 return synthIcon.getIconHeight(context); 656 } 657 } 658 }