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