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 35 /** 36 * Wrapper for primitive graphics calls. 37 * 38 * @since 1.5 39 * @author Scott Violet 40 */ 41 public class SynthGraphicsUtils { 42 // These are used in the text painting code to avoid allocating a bunch of 43 // garbage. 44 private Rectangle paintIconR = new Rectangle(); 45 private Rectangle paintTextR = new Rectangle(); 46 private Rectangle paintViewR = new Rectangle(); 47 private Insets paintInsets = new Insets(0, 0, 0, 0); 48 49 // These Rectangles/Insets are used in the text size calculation to avoid a 50 // a bunch of garbage. 51 private Rectangle iconR = new Rectangle(); 52 private Rectangle textR = new Rectangle(); 53 private Rectangle viewR = new Rectangle(); 54 private Insets viewSizingInsets = new Insets(0, 0, 0, 0); 55 56 /** 57 * Creates a <code>SynthGraphicsUtils</code>. 58 */ 59 public SynthGraphicsUtils() { 60 } 61 62 /** 63 * Draws a line between the two end points. 64 * 65 * @param context Identifies hosting region. 66 * @param paintKey Identifies the portion of the component being asked 67 * to paint, may be null. 68 * @param g Graphics object to paint to 69 * @param x1 x origin 70 * @param y1 y origin 71 * @param x2 x destination 72 * @param y2 y destination 73 */ 74 public void drawLine(SynthContext context, Object paintKey, 75 Graphics g, int x1, int y1, int x2, int y2) { 76 g.drawLine(x1, y1, x2, y2); 77 } 78 79 /** 80 * Draws a line between the two end points. 81 * <p>This implementation supports only one line style key, 82 * <code>"dashed"</code>. The <code>"dashed"</code> line style is applied 83 * only to vertical and horizontal lines. 84 * <p>Specifying <code>null</code> or any key different from 85 * <code>"dashed"</code> will draw solid lines. 86 * 87 * @param context identifies hosting region 88 * @param paintKey identifies the portion of the component being asked 89 * to paint, may be null 90 * @param g Graphics object to paint to 91 * @param x1 x origin 92 * @param y1 y origin 93 * @param x2 x destination 94 * @param y2 y destination 95 * @param styleKey identifies the requested style of the line (e.g. "dashed") 96 * @since 1.6 97 */ 98 public void drawLine(SynthContext context, Object paintKey, 99 Graphics g, int x1, int y1, int x2, int y2, 100 Object styleKey) { 101 if ("dashed".equals(styleKey)) { 102 // draw vertical line 103 if (x1 == x2) { 104 y1 += (y1 % 2); 105 106 for (int y = y1; y <= y2; y+=2) { 107 g.drawLine(x1, y, x2, y); 108 } 109 // draw horizontal line 110 } else if (y1 == y2) { 111 x1 += (x1 % 2); 112 113 for (int x = x1; x <= x2; x+=2) { 114 g.drawLine(x, y1, x, y2); 115 } 116 // oblique lines are not supported 117 } 118 } else { 119 drawLine(context, paintKey, g, x1, y1, x2, y2); 120 } 121 } 122 123 /** 124 * Lays out text and an icon returning, by reference, the location to 125 * place the icon and text. 126 * 127 * @param ss SynthContext 128 * @param fm FontMetrics for the Font to use, this may be ignored 129 * @param text Text to layout 130 * @param icon Icon to layout 131 * @param hAlign horizontal alignment 132 * @param vAlign vertical alignment 133 * @param hTextPosition horizontal text position 134 * @param vTextPosition vertical text position 135 * @param viewR Rectangle to layout text and icon in. 136 * @param iconR Rectangle to place icon bounds in 137 * @param textR Rectangle to place text in 138 * @param iconTextGap gap between icon and text 139 * 140 * @return by reference, the location to 141 * place the icon and text. 142 */ 143 public String layoutText(SynthContext ss, FontMetrics fm, 144 String text, Icon icon, int hAlign, 145 int vAlign, int hTextPosition, 146 int vTextPosition, Rectangle viewR, 147 Rectangle iconR, Rectangle textR, int iconTextGap) { 148 if (icon instanceof SynthIcon) { 149 SynthIconWrapper wrapper = SynthIconWrapper.get((SynthIcon)icon, 150 ss); 151 String formattedText = SwingUtilities.layoutCompoundLabel( 152 ss.getComponent(), fm, text, wrapper, vAlign, hAlign, 153 vTextPosition, hTextPosition, viewR, iconR, textR, 154 iconTextGap); 155 SynthIconWrapper.release(wrapper); 156 return formattedText; 157 } 158 return SwingUtilities.layoutCompoundLabel( 159 ss.getComponent(), fm, text, icon, vAlign, hAlign, 160 vTextPosition, hTextPosition, viewR, iconR, textR, 161 iconTextGap); 162 } 163 164 /** 165 * Returns the size of the passed in string. 166 * 167 * @param ss SynthContext 168 * @param font Font to use 169 * @param metrics FontMetrics, may be ignored 170 * @param text Text to get size of. 171 * 172 * @return the size of the passed in string. 173 */ 174 public int computeStringWidth(SynthContext ss, Font font, 175 FontMetrics metrics, String text) { 176 JComponent comp = ss.getComponent(); 177 return SwingUtilities2.getTextUIDrawing(comp) 178 .getStringWidth(comp, metrics, 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(getIconWidth(icon, ss) + dx, 291 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.getTextUIDrawing(c) 352 .drawStringUnderlineCharAt(c, g, text, 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 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 * Returns the icon's width. 428 * The {@code getIconWidth(context)} method is called for {@code SynthIcon}. 429 * 430 * @param icon the icon 431 * @param context {@code SynthContext} requesting the icon, may be null. 432 * @return an int specifying the width of the icon. 433 */ 434 public static int getIconWidth(Icon icon, SynthContext context) { 435 if (icon == null) { 436 return 0; 437 } 438 if (icon instanceof SynthIcon) { 439 return ((SynthIcon) icon).getIconWidth(context); 440 } 441 return icon.getIconWidth(); 442 } 443 444 /** 445 * Returns the icon's height. 446 * The {@code getIconHeight(context)} method is called for {@code SynthIcon}. 447 * 448 * @param icon the icon 449 * @param context {@code SynthContext} requesting the icon, may be null. 450 * @return an int specifying the height of the icon. 451 */ 452 public static int getIconHeight(Icon icon, SynthContext context) { 453 if (icon == null) { 454 return 0; 455 } 456 if (icon instanceof SynthIcon) { 457 return ((SynthIcon) icon).getIconHeight(context); 458 } 459 return icon.getIconHeight(); 460 } 461 462 /** 463 * Paints the icon. The {@code paintIcon(context, g, x, y, width, height)} 464 * method is called for {@code SynthIcon}. 465 * 466 * @param icon the icon 467 * @param context identifies hosting region, may be null. 468 * @param g the graphics context 469 * @param x the x location to paint to 470 * @param y the y location to paint to 471 * @param width the width of the region to paint to, may be 0 472 * @param height the height of the region to paint to, may be 0 473 */ 474 public static void paintIcon(Icon icon, SynthContext context, Graphics g, 475 int x, int y, int width, int height) { 476 if (icon instanceof SynthIcon) { 477 ((SynthIcon) icon).paintIcon(context, g, x, y, width, height); 478 } else if (icon != null) { 479 icon.paintIcon(context.getComponent(), g, x, y); 480 } 481 } 482 483 /** 484 * A quick note about how preferred sizes are calculated... Generally 485 * speaking, SynthPopupMenuUI will run through the list of its children 486 * (from top to bottom) and ask each for its preferred size. Each menu 487 * item will add up the max width of each element (icons, text, 488 * accelerator spacing, accelerator text or arrow icon) encountered thus 489 * far, so by the time all menu items have been calculated, we will 490 * know the maximum (preferred) menu item size for that popup menu. 491 * Later when it comes time to paint each menu item, we can use those 492 * same accumulated max element sizes in order to layout the item. 493 */ 494 static Dimension getPreferredMenuItemSize(SynthContext context, 495 SynthContext accContext, JComponent c, 496 Icon checkIcon, Icon arrowIcon, int defaultTextIconGap, 497 String acceleratorDelimiter, boolean useCheckAndArrow, 498 String propertyPrefix) { 499 500 JMenuItem mi = (JMenuItem) c; 501 SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper( 502 context, accContext, mi, checkIcon, arrowIcon, 503 MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap, 504 acceleratorDelimiter, SynthLookAndFeel.isLeftToRight(mi), 505 useCheckAndArrow, propertyPrefix); 506 507 Dimension result = new Dimension(); 508 509 // Calculate the result width 510 int gap = lh.getGap(); 511 result.width = 0; 512 MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(), gap, result); 513 MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), gap, result); 514 MenuItemLayoutHelper.addWidth(lh.getMaxAccOrArrowWidth(), 5 * gap, result); 515 // The last gap is unnecessary 516 result.width -= gap; 517 518 // Calculate the result height 519 result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(), 520 lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(), 521 lh.getArrowSize().getHeight()); 522 523 // Take into account menu item insets 524 Insets insets = lh.getMenuItem().getInsets(); 525 if (insets != null) { 526 result.width += insets.left + insets.right; 527 result.height += insets.top + insets.bottom; 528 } 529 530 // if the width is even, bump it up one. This is critical 531 // for the focus dash lhne to draw properly 532 if (result.width % 2 == 0) { 533 result.width++; 534 } 535 536 // if the height is even, bump it up one. This is critical 537 // for the text to center properly 538 if (result.height % 2 == 0) { 539 result.height++; 540 } 541 542 return result; 543 } 544 545 static void applyInsets(Rectangle rect, Insets insets, boolean leftToRight) { 546 if (insets != null) { 547 rect.x += (leftToRight ? insets.left : insets.right); 548 rect.y += insets.top; 549 rect.width -= (leftToRight ? insets.right : insets.left) + rect.x; 550 rect.height -= (insets.bottom + rect.y); 551 } 552 } 553 554 static void paint(SynthContext context, SynthContext accContext, Graphics g, 555 Icon checkIcon, Icon arrowIcon, String acceleratorDelimiter, 556 int defaultTextIconGap, String propertyPrefix) { 557 JMenuItem mi = (JMenuItem) context.getComponent(); 558 SynthStyle style = context.getStyle(); 559 g.setFont(style.getFont(context)); 560 561 Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); 562 boolean leftToRight = SynthLookAndFeel.isLeftToRight(mi); 563 applyInsets(viewRect, mi.getInsets(), leftToRight); 564 565 SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper( 566 context, accContext, mi, checkIcon, arrowIcon, viewRect, 567 defaultTextIconGap, acceleratorDelimiter, leftToRight, 568 MenuItemLayoutHelper.useCheckAndArrow(mi), propertyPrefix); 569 MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); 570 571 paintMenuItem(g, lh, lr); 572 } 573 574 static void paintMenuItem(Graphics g, SynthMenuItemLayoutHelper lh, 575 MenuItemLayoutHelper.LayoutResult lr) { 576 // Save original graphics font and color 577 Font holdf = g.getFont(); 578 Color holdc = g.getColor(); 579 580 paintCheckIcon(g, lh, lr); 581 paintIcon(g, lh, lr); 582 paintText(g, lh, lr); 583 paintAccText(g, lh, lr); 584 paintArrowIcon(g, lh, lr); 585 586 // Restore original graphics font and color 587 g.setColor(holdc); 588 g.setFont(holdf); 589 } 590 591 static void paintBackground(Graphics g, SynthMenuItemLayoutHelper lh) { 592 paintBackground(lh.getContext(), g, lh.getMenuItem()); 593 } 594 595 static void paintBackground(SynthContext context, Graphics g, JComponent c) { 596 context.getPainter().paintMenuItemBackground(context, g, 0, 0, 597 c.getWidth(), c.getHeight()); 598 } 599 600 static void paintIcon(Graphics g, SynthMenuItemLayoutHelper lh, 601 MenuItemLayoutHelper.LayoutResult lr) { 602 if (lh.getIcon() != null) { 603 Icon icon; 604 JMenuItem mi = lh.getMenuItem(); 605 ButtonModel model = mi.getModel(); 606 if (!model.isEnabled()) { 607 icon = mi.getDisabledIcon(); 608 } else if (model.isPressed() && model.isArmed()) { 609 icon = mi.getPressedIcon(); 610 if (icon == null) { 611 // Use default icon 612 icon = mi.getIcon(); 613 } 614 } else { 615 icon = mi.getIcon(); 616 } 617 618 if (icon != null) { 619 Rectangle iconRect = lr.getIconRect(); 620 paintIcon(icon, lh.getContext(), g, iconRect.x, 621 iconRect.y, iconRect.width, iconRect.height); 622 } 623 } 624 } 625 626 static void paintCheckIcon(Graphics g, SynthMenuItemLayoutHelper lh, 627 MenuItemLayoutHelper.LayoutResult lr) { 628 if (lh.getCheckIcon() != null) { 629 Rectangle checkRect = lr.getCheckRect(); 630 paintIcon(lh.getCheckIcon(), lh.getContext(), g, 631 checkRect.x, checkRect.y, checkRect.width, checkRect.height); 632 } 633 } 634 635 static void paintAccText(Graphics g, SynthMenuItemLayoutHelper lh, 636 MenuItemLayoutHelper.LayoutResult lr) { 637 String accText = lh.getAccText(); 638 if (accText != null && !accText.equals("")) { 639 g.setColor(lh.getAccStyle().getColor(lh.getAccContext(), 640 ColorType.TEXT_FOREGROUND)); 641 g.setFont(lh.getAccStyle().getFont(lh.getAccContext())); 642 lh.getAccGraphicsUtils().paintText(lh.getAccContext(), g, accText, 643 lr.getAccRect().x, lr.getAccRect().y, -1); 644 } 645 } 646 647 static void paintText(Graphics g, SynthMenuItemLayoutHelper lh, 648 MenuItemLayoutHelper.LayoutResult lr) { 649 if (!lh.getText().equals("")) { 650 if (lh.getHtmlView() != null) { 651 // Text is HTML 652 lh.getHtmlView().paint(g, lr.getTextRect()); 653 } else { 654 // Text isn't HTML 655 g.setColor(lh.getStyle().getColor( 656 lh.getContext(), ColorType.TEXT_FOREGROUND)); 657 g.setFont(lh.getStyle().getFont(lh.getContext())); 658 lh.getGraphicsUtils().paintText(lh.getContext(), g, lh.getText(), 659 lr.getTextRect().x, lr.getTextRect().y, 660 lh.getMenuItem().getDisplayedMnemonicIndex()); 661 } 662 } 663 } 664 665 static void paintArrowIcon(Graphics g, SynthMenuItemLayoutHelper lh, 666 MenuItemLayoutHelper.LayoutResult lr) { 667 if (lh.getArrowIcon() != null) { 668 Rectangle arrowRect = lr.getArrowRect(); 669 paintIcon(lh.getArrowIcon(), lh.getContext(), g, 670 arrowRect.x, arrowRect.y, arrowRect.width, arrowRect.height); 671 } 672 } 673 674 /** 675 * Wraps a SynthIcon around the Icon interface, forwarding calls to 676 * the SynthIcon with a given SynthContext. 677 */ 678 private static class SynthIconWrapper implements Icon { 679 private static final java.util.List<SynthIconWrapper> CACHE = new java.util.ArrayList<SynthIconWrapper>(1); 680 681 private SynthIcon synthIcon; 682 private SynthContext context; 683 684 static SynthIconWrapper get(SynthIcon icon, SynthContext context) { 685 synchronized(CACHE) { 686 int size = CACHE.size(); 687 if (size > 0) { 688 SynthIconWrapper wrapper = CACHE.remove(size - 1); 689 wrapper.reset(icon, context); 690 return wrapper; 691 } 692 } 693 return new SynthIconWrapper(icon, context); 694 } 695 696 static void release(SynthIconWrapper wrapper) { 697 wrapper.reset(null, null); 698 synchronized(CACHE) { 699 CACHE.add(wrapper); 700 } 701 } 702 703 SynthIconWrapper(SynthIcon icon, SynthContext context) { 704 reset(icon, context); 705 } 706 707 void reset(SynthIcon icon, SynthContext context) { 708 synthIcon = icon; 709 this.context = context; 710 } 711 712 public void paintIcon(Component c, Graphics g, int x, int y) { 713 // This is a noop as this should only be for sizing calls. 714 } 715 716 public int getIconWidth() { 717 return synthIcon.getIconWidth(context); 718 } 719 720 public int getIconHeight() { 721 return synthIcon.getIconHeight(context); 722 } 723 } 724 }