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 paintText(ss, g, text, bounds.x, bounds.y, mnemonicIndex); 330 } 331 332 /** 333 * Paints text at the specified location. This will not attempt to 334 * render the text as html nor will it offset by the insets of the 335 * component. 336 * 337 * @param ss SynthContext 338 * @param g Graphics used to render string in. 339 * @param text Text to render 340 * @param x X location to draw text at. 341 * @param y Upper left corner to draw text at. 342 * @param mnemonicIndex Index to draw string at. 343 */ 344 public void paintText(SynthContext ss, Graphics g, String text, 345 int x, int y, int mnemonicIndex) { 346 if (text != null) { 347 JComponent c = ss.getComponent(); 348 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g); 349 y += fm.getAscent(); 350 SwingUtilities2.drawStringUnderlineCharAt(c, g, text, 351 mnemonicIndex, x, y); 352 } 353 } 354 355 /** 356 * Paints an icon and text. This will render the text as html, if 357 * necessary, and offset the location by the insets of the component. 358 * 359 * @param ss SynthContext 360 * @param g Graphics to render string and icon into 361 * @param text Text to layout 362 * @param icon Icon to layout 363 * @param hAlign horizontal alignment 364 * @param vAlign vertical alignment 365 * @param hTextPosition horizontal text position 366 * @param vTextPosition vertical text position 367 * @param iconTextGap gap between icon and text 368 * @param mnemonicIndex Index into text to render the mnemonic at, -1 369 * indicates no mnemonic. 370 * @param textOffset Amount to offset the text when painting 371 */ 372 public void paintText(SynthContext ss, Graphics g, String text, 373 Icon icon, int hAlign, int vAlign, int hTextPosition, 374 int vTextPosition, int iconTextGap, int mnemonicIndex, 375 int textOffset) { 376 if ((icon == null) && (text == null)) { 377 return; 378 } 379 JComponent c = ss.getComponent(); 380 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g); 381 Insets insets = SynthLookAndFeel.getPaintingInsets(ss, paintInsets); 382 383 paintViewR.x = insets.left; 384 paintViewR.y = insets.top; 385 paintViewR.width = c.getWidth() - (insets.left + insets.right); 386 paintViewR.height = c.getHeight() - (insets.top + insets.bottom); 387 388 paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0; 389 paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0; 390 391 String clippedText = 392 layoutText(ss, fm, text, icon, hAlign, vAlign, 393 hTextPosition, vTextPosition, paintViewR, paintIconR, 394 paintTextR, iconTextGap); 395 396 if (icon != null) { 397 Color color = g.getColor(); 398 399 if (ss.getStyle().getBoolean(ss, "TableHeader.alignSorterArrow", false) && 400 "TableHeader.renderer".equals(c.getName())) { 401 paintIconR.x = paintViewR.width - paintIconR.width; 402 } else { 403 paintIconR.x += textOffset; 404 } 405 paintIconR.y += textOffset; 406 paintIcon(icon, ss, g, paintIconR.x, paintIconR.y, 407 paintIconR.width, paintIconR.height); 408 g.setColor(color); 409 } 410 411 if (text != null) { 412 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 413 414 if (v != null) { 415 v.paint(g, paintTextR); 416 } else { 417 paintTextR.x += textOffset; 418 paintTextR.y += textOffset; 419 420 paintText(ss, g, clippedText, paintTextR, mnemonicIndex); 421 } 422 } 423 } 424 425 /** 426 * Returns the icon's width. 427 * The {@code getIconWidth(context)} method is called for {@code SynthIcon}. 428 * 429 * @param icon the icon 430 * @param context {@code SynthContext} requesting the icon, may be null. 431 * @return an int specifying the width of the icon. 432 */ 433 public static int getIconWidth(Icon icon, SynthContext context) { 434 if (icon == null) { 435 return 0; 436 } 437 if (icon instanceof SynthIcon) { 438 return ((SynthIcon) icon).getIconWidth(context); 439 } 440 return icon.getIconWidth(); 441 } 442 443 /** 444 * Returns the icon's height. 445 * The {@code getIconHeight(context)} method is called for {@code SynthIcon}. 446 * 447 * @param icon the icon 448 * @param context {@code SynthContext} requesting the icon, may be null. 449 * @return an int specifying the height of the icon. 450 */ 451 public static int getIconHeight(Icon icon, SynthContext context) { 452 if (icon == null) { 453 return 0; 454 } 455 if (icon instanceof SynthIcon) { 456 return ((SynthIcon) icon).getIconHeight(context); 457 } 458 return icon.getIconHeight(); 459 } 460 461 /** 462 * Paints the icon. The {@code paintIcon(context, g, x, y, width, height)} 463 * method is called for {@code SynthIcon}. 464 * 465 * @param icon the icon 466 * @param context identifies hosting region, may be null. 467 * @param g the graphics context 468 * @param x the x location to paint to 469 * @param y the y location to paint to 470 * @param width the width of the region to paint to, may be 0 471 * @param height the height of the region to paint to, may be 0 472 */ 473 public static void paintIcon(Icon icon, SynthContext context, Graphics g, 474 int x, int y, int width, int height) { 475 if (icon instanceof SynthIcon) { 476 ((SynthIcon) icon).paintIcon(context, g, x, y, width, height); 477 } else if (icon != null) { 478 icon.paintIcon(context.getComponent(), g, x, y); 479 } 480 } 481 482 /** 483 * A quick note about how preferred sizes are calculated... Generally 484 * speaking, SynthPopupMenuUI will run through the list of its children 485 * (from top to bottom) and ask each for its preferred size. Each menu 486 * item will add up the max width of each element (icons, text, 487 * accelerator spacing, accelerator text or arrow icon) encountered thus 488 * far, so by the time all menu items have been calculated, we will 489 * know the maximum (preferred) menu item size for that popup menu. 490 * Later when it comes time to paint each menu item, we can use those 491 * same accumulated max element sizes in order to layout the item. 492 */ 493 static Dimension getPreferredMenuItemSize(SynthContext context, 494 SynthContext accContext, JComponent c, 495 Icon checkIcon, Icon arrowIcon, int defaultTextIconGap, 496 String acceleratorDelimiter, boolean useCheckAndArrow, 497 String propertyPrefix) { 498 499 JMenuItem mi = (JMenuItem) c; 500 SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper( 501 context, accContext, mi, checkIcon, arrowIcon, 502 MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap, 503 acceleratorDelimiter, SynthLookAndFeel.isLeftToRight(mi), 504 useCheckAndArrow, propertyPrefix); 505 506 Dimension result = new Dimension(); 507 508 // Calculate the result width 509 int gap = lh.getGap(); 510 result.width = 0; 511 MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(), gap, result); 512 MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), gap, result); 513 MenuItemLayoutHelper.addWidth(lh.getMaxAccOrArrowWidth(), 5 * gap, result); 514 // The last gap is unnecessary 515 result.width -= gap; 516 517 // Calculate the result height 518 result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(), 519 lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(), 520 lh.getArrowSize().getHeight()); 521 522 // Take into account menu item insets 523 Insets insets = lh.getMenuItem().getInsets(); 524 if (insets != null) { 525 result.width += insets.left + insets.right; 526 result.height += insets.top + insets.bottom; 527 } 528 529 // if the width is even, bump it up one. This is critical 530 // for the focus dash lhne to draw properly 531 if (result.width % 2 == 0) { 532 result.width++; 533 } 534 535 // if the height is even, bump it up one. This is critical 536 // for the text to center properly 537 if (result.height % 2 == 0) { 538 result.height++; 539 } 540 541 return result; 542 } 543 544 static void applyInsets(Rectangle rect, Insets insets, boolean leftToRight) { 545 if (insets != null) { 546 rect.x += (leftToRight ? insets.left : insets.right); 547 rect.y += insets.top; 548 rect.width -= (leftToRight ? insets.right : insets.left) + rect.x; 549 rect.height -= (insets.bottom + rect.y); 550 } 551 } 552 553 static void paint(SynthContext context, SynthContext accContext, Graphics g, 554 Icon checkIcon, Icon arrowIcon, String acceleratorDelimiter, 555 int defaultTextIconGap, String propertyPrefix) { 556 JMenuItem mi = (JMenuItem) context.getComponent(); 557 SynthStyle style = context.getStyle(); 558 g.setFont(style.getFont(context)); 559 560 Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); 561 boolean leftToRight = SynthLookAndFeel.isLeftToRight(mi); 562 applyInsets(viewRect, mi.getInsets(), leftToRight); 563 564 SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper( 565 context, accContext, mi, checkIcon, arrowIcon, viewRect, 566 defaultTextIconGap, acceleratorDelimiter, leftToRight, 567 MenuItemLayoutHelper.useCheckAndArrow(mi), propertyPrefix); 568 MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); 569 570 paintMenuItem(g, lh, lr); 571 } 572 573 static void paintMenuItem(Graphics g, SynthMenuItemLayoutHelper lh, 574 MenuItemLayoutHelper.LayoutResult lr) { 575 // Save original graphics font and color 576 Font holdf = g.getFont(); 577 Color holdc = g.getColor(); 578 579 paintCheckIcon(g, lh, lr); 580 paintIcon(g, lh, lr); 581 paintText(g, lh, lr); 582 paintAccText(g, lh, lr); 583 paintArrowIcon(g, lh, lr); 584 585 // Restore original graphics font and color 586 g.setColor(holdc); 587 g.setFont(holdf); 588 } 589 590 static void paintBackground(Graphics g, SynthMenuItemLayoutHelper lh) { 591 paintBackground(lh.getContext(), g, lh.getMenuItem()); 592 } 593 594 static void paintBackground(SynthContext context, Graphics g, JComponent c) { 595 context.getPainter().paintMenuItemBackground(context, g, 0, 0, 596 c.getWidth(), c.getHeight()); 597 } 598 599 static void paintIcon(Graphics g, SynthMenuItemLayoutHelper lh, 600 MenuItemLayoutHelper.LayoutResult lr) { 601 if (lh.getIcon() != null) { 602 Icon icon; 603 JMenuItem mi = lh.getMenuItem(); 604 ButtonModel model = mi.getModel(); 605 if (!model.isEnabled()) { 606 icon = mi.getDisabledIcon(); 607 } else if (model.isPressed() && model.isArmed()) { 608 icon = mi.getPressedIcon(); 609 if (icon == null) { 610 // Use default icon 611 icon = mi.getIcon(); 612 } 613 } else { 614 icon = mi.getIcon(); 615 } 616 617 if (icon != null) { 618 Rectangle iconRect = lr.getIconRect(); 619 paintIcon(icon, lh.getContext(), g, iconRect.x, 620 iconRect.y, iconRect.width, iconRect.height); 621 } 622 } 623 } 624 625 static void paintCheckIcon(Graphics g, SynthMenuItemLayoutHelper lh, 626 MenuItemLayoutHelper.LayoutResult lr) { 627 if (lh.getCheckIcon() != null) { 628 Rectangle checkRect = lr.getCheckRect(); 629 paintIcon(lh.getCheckIcon(), lh.getContext(), g, 630 checkRect.x, checkRect.y, checkRect.width, checkRect.height); 631 } 632 } 633 634 static void paintAccText(Graphics g, SynthMenuItemLayoutHelper lh, 635 MenuItemLayoutHelper.LayoutResult lr) { 636 String accText = lh.getAccText(); 637 if (accText != null && !accText.equals("")) { 638 g.setColor(lh.getAccStyle().getColor(lh.getAccContext(), 639 ColorType.TEXT_FOREGROUND)); 640 g.setFont(lh.getAccStyle().getFont(lh.getAccContext())); 641 lh.getAccGraphicsUtils().paintText(lh.getAccContext(), g, accText, 642 lr.getAccRect().x, lr.getAccRect().y, -1); 643 } 644 } 645 646 static void paintText(Graphics g, SynthMenuItemLayoutHelper lh, 647 MenuItemLayoutHelper.LayoutResult lr) { 648 if (!lh.getText().equals("")) { 649 if (lh.getHtmlView() != null) { 650 // Text is HTML 651 lh.getHtmlView().paint(g, lr.getTextRect()); 652 } else { 653 // Text isn't HTML 654 g.setColor(lh.getStyle().getColor( 655 lh.getContext(), ColorType.TEXT_FOREGROUND)); 656 g.setFont(lh.getStyle().getFont(lh.getContext())); 657 lh.getGraphicsUtils().paintText(lh.getContext(), g, lh.getText(), 658 lr.getTextRect().x, lr.getTextRect().y, 659 lh.getMenuItem().getDisplayedMnemonicIndex()); 660 } 661 } 662 } 663 664 static void paintArrowIcon(Graphics g, SynthMenuItemLayoutHelper lh, 665 MenuItemLayoutHelper.LayoutResult lr) { 666 if (lh.getArrowIcon() != null) { 667 Rectangle arrowRect = lr.getArrowRect(); 668 paintIcon(lh.getArrowIcon(), lh.getContext(), g, 669 arrowRect.x, arrowRect.y, arrowRect.width, arrowRect.height); 670 } 671 } 672 673 /** 674 * Wraps a SynthIcon around the Icon interface, forwarding calls to 675 * the SynthIcon with a given SynthContext. 676 */ 677 private static class SynthIconWrapper implements Icon { 678 private static final java.util.List<SynthIconWrapper> CACHE = new java.util.ArrayList<SynthIconWrapper>(1); 679 680 private SynthIcon synthIcon; 681 private SynthContext context; 682 683 static SynthIconWrapper get(SynthIcon icon, SynthContext context) { 684 synchronized(CACHE) { 685 int size = CACHE.size(); 686 if (size > 0) { 687 SynthIconWrapper wrapper = CACHE.remove(size - 1); 688 wrapper.reset(icon, context); 689 return wrapper; 690 } 691 } 692 return new SynthIconWrapper(icon, context); 693 } 694 695 static void release(SynthIconWrapper wrapper) { 696 wrapper.reset(null, null); 697 synchronized(CACHE) { 698 CACHE.add(wrapper); 699 } 700 } 701 702 SynthIconWrapper(SynthIcon icon, SynthContext context) { 703 reset(icon, context); 704 } 705 706 void reset(SynthIcon icon, SynthContext context) { 707 synthIcon = icon; 708 this.context = context; 709 } 710 711 public void paintIcon(Component c, Graphics g, int x, int y) { 712 // This is a noop as this should only be for sizing calls. 713 } 714 715 public int getIconWidth() { 716 return synthIcon.getIconWidth(context); 717 } 718 719 public int getIconHeight() { 720 return synthIcon.getIconHeight(context); 721 } 722 } 723 }