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 }