001: /*
002: * Copyright (c) 2001-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.looks.common;
032:
033: import java.awt.*;
034: import java.awt.event.KeyEvent;
035:
036: import javax.swing.*;
037: import javax.swing.plaf.basic.BasicHTML;
038: import javax.swing.text.View;
039:
040: import com.jgoodies.looks.Options;
041:
042: /**
043: * Renders and lays out menu items.
044: *
045: * @author Karsten Lentzsch
046: * @version $Revision: 1.9 $
047: */
048:
049: public class MenuItemRenderer {
050:
051: /*
052: * Implementation note: The protected visibility prevents
053: * the String value from being encrypted by the obfuscator.
054: * An encrypted String key would break the client property lookup
055: * in the #paint method below.
056: */
057: protected static final String HTML_KEY = BasicHTML.propertyKey;
058:
059: /* Client Property keys for text and accelerator text widths */
060: static final String MAX_TEXT_WIDTH = "maxTextWidth";
061: static final String MAX_ACC_WIDTH = "maxAccWidth";
062:
063: private static final Icon NO_ICON = new NullIcon();
064:
065: static Rectangle zeroRect = new Rectangle(0, 0, 0, 0);
066: static Rectangle iconRect = new Rectangle();
067: static Rectangle textRect = new Rectangle();
068: static Rectangle acceleratorRect = new Rectangle();
069: static Rectangle checkIconRect = new Rectangle();
070: static Rectangle arrowIconRect = new Rectangle();
071: static Rectangle viewRect = new Rectangle(Short.MAX_VALUE,
072: Short.MAX_VALUE);
073: static Rectangle r = new Rectangle();
074:
075: private final JMenuItem menuItem;
076: private final boolean iconBorderEnabled; // when selected or pressed.
077: private final Font acceleratorFont;
078: private final Color selectionForeground;
079: private final Color disabledForeground;
080: private final Color acceleratorForeground;
081: private final Color acceleratorSelectionForeground;
082:
083: private final String acceleratorDelimiter;
084: private final Icon fillerIcon;
085:
086: /**
087: * Constructs a MenuItemRenderer for the specified menu item and settings.
088: */
089: public MenuItemRenderer(JMenuItem menuItem,
090: boolean iconBorderEnabled, Font acceleratorFont,
091: Color selectionForeground, Color disabledForeground,
092: Color acceleratorForeground,
093: Color acceleratorSelectionForeground) {
094: this .menuItem = menuItem;
095: this .iconBorderEnabled = iconBorderEnabled;
096: this .acceleratorFont = acceleratorFont;
097: this .selectionForeground = selectionForeground;
098: this .disabledForeground = disabledForeground;
099: this .acceleratorForeground = acceleratorForeground;
100: this .acceleratorSelectionForeground = acceleratorSelectionForeground;
101: this .acceleratorDelimiter = UIManager
102: .getString("MenuItem.acceleratorDelimiter");
103: this .fillerIcon = new MinimumSizedIcon();
104: }
105:
106: /**
107: * Looks up and answers the appropriate menu item icon.
108: */
109: private Icon getIcon(JMenuItem aMenuItem, Icon defaultIcon) {
110: Icon icon = aMenuItem.getIcon();
111: if (icon == null)
112: return defaultIcon;
113:
114: ButtonModel model = aMenuItem.getModel();
115: if (!model.isEnabled()) {
116: return model.isSelected() ? aMenuItem
117: .getDisabledSelectedIcon() : aMenuItem
118: .getDisabledIcon();
119: } else if (model.isPressed() && model.isArmed()) {
120: Icon pressedIcon = aMenuItem.getPressedIcon();
121: return pressedIcon != null ? pressedIcon : icon;
122: } else if (model.isSelected()) {
123: Icon selectedIcon = aMenuItem.getSelectedIcon();
124: return selectedIcon != null ? selectedIcon : icon;
125: } else
126: return icon;
127: }
128:
129: /**
130: * Checks and answers if the menu item has a custom icon.
131: */
132: private boolean hasCustomIcon() {
133: return getIcon(menuItem, null) != null;
134: }
135:
136: /**
137: * Answers the wrapped icon.
138: */
139: private Icon getWrappedIcon(Icon icon) {
140: if (hideIcons())
141: return NO_ICON;
142: if (icon == null)
143: return fillerIcon;
144: return iconBorderEnabled && hasCustomIcon() ? new MinimumSizedCheckIcon(
145: icon, menuItem)
146: : new MinimumSizedIcon(icon);
147: }
148:
149: private void resetRects() {
150: iconRect.setBounds(zeroRect);
151: textRect.setBounds(zeroRect);
152: acceleratorRect.setBounds(zeroRect);
153: checkIconRect.setBounds(zeroRect);
154: arrowIconRect.setBounds(zeroRect);
155: viewRect.setBounds(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
156: r.setBounds(zeroRect);
157: }
158:
159: public Dimension getPreferredMenuItemSize(JComponent c,
160: Icon checkIcon, Icon arrowIcon, int defaultTextIconGap) {
161:
162: JMenuItem b = (JMenuItem) c;
163: String text = b.getText();
164: KeyStroke accelerator = b.getAccelerator();
165: String acceleratorText = "";
166:
167: if (accelerator != null) {
168: int modifiers = accelerator.getModifiers();
169: if (modifiers > 0) {
170: acceleratorText = KeyEvent
171: .getKeyModifiersText(modifiers);
172: acceleratorText += acceleratorDelimiter;
173: }
174: int keyCode = accelerator.getKeyCode();
175: if (keyCode != 0) {
176: acceleratorText += KeyEvent.getKeyText(keyCode);
177: } else {
178: acceleratorText += accelerator.getKeyChar();
179: }
180: }
181:
182: Font font = b.getFont();
183: FontMetrics fm = b.getFontMetrics(font);
184: FontMetrics fmAccel = b.getFontMetrics(acceleratorFont);
185:
186: resetRects();
187:
188: Icon wrappedIcon = getWrappedIcon(getIcon(menuItem, checkIcon));
189: Icon wrappedArrowIcon = new MinimumSizedIcon(arrowIcon);
190: Icon icon = wrappedIcon.getIconHeight() > fillerIcon
191: .getIconHeight() ? wrappedIcon : null;
192:
193: layoutMenuItem(fm,
194: text,
195: fmAccel,
196: acceleratorText,
197: //icon, checkIcon,
198: icon,
199: wrappedIcon,
200: wrappedArrowIcon, //arrowIcon,
201: b.getVerticalAlignment(), b.getHorizontalAlignment(), b
202: .getVerticalTextPosition(), b
203: .getHorizontalTextPosition(), viewRect,
204: iconRect, textRect, acceleratorRect, checkIconRect,
205: arrowIconRect, text == null ? 0 : defaultTextIconGap,
206: defaultTextIconGap);
207: // find the union of the icon and text rects
208: r.setBounds(textRect);
209: r = SwingUtilities.computeUnion(iconRect.x, iconRect.y,
210: iconRect.width, iconRect.height, r);
211: // r = iconRect.union(textRect);
212:
213: // To make the accelerator texts appear in a column, find the widest MenuItem text
214: // and the widest accelerator text.
215:
216: //Get the parent, which stores the information.
217: Container parent = menuItem.getParent();
218:
219: //Check the parent, and see that it is not a top-level menu.
220: if (parent != null
221: && parent instanceof JComponent
222: && !(menuItem instanceof JMenu && ((JMenu) menuItem)
223: .isTopLevelMenu())) {
224: JComponent p = (JComponent) parent;
225:
226: //Get widest text so far from parent, if no one exists null is returned.
227: Integer maxTextWidth = (Integer) p
228: .getClientProperty(MAX_TEXT_WIDTH);
229: Integer maxAccWidth = (Integer) p
230: .getClientProperty(MAX_ACC_WIDTH);
231:
232: int maxTextValue = maxTextWidth != null ? maxTextWidth
233: .intValue() : 0;
234: int maxAccValue = maxAccWidth != null ? maxAccWidth
235: .intValue() : 0;
236:
237: //Compare the text widths, and adjust the r.width to the widest.
238: if (r.width < maxTextValue) {
239: r.width = maxTextValue;
240: } else {
241: p.putClientProperty(MAX_TEXT_WIDTH,
242: new Integer(r.width));
243: }
244:
245: //Compare the accelarator widths.
246: if (acceleratorRect.width > maxAccValue) {
247: maxAccValue = acceleratorRect.width;
248: p.putClientProperty(MAX_ACC_WIDTH, new Integer(
249: acceleratorRect.width));
250: }
251:
252: //Add on the widest accelerator
253: r.width += maxAccValue;
254: r.width += 10;
255: }
256:
257: if (useCheckAndArrow()) {
258: // Add in the checkIcon
259: r.width += checkIconRect.width;
260: r.width += defaultTextIconGap;
261:
262: // Add in the arrowIcon
263: r.width += defaultTextIconGap;
264: r.width += arrowIconRect.width;
265: }
266:
267: r.width += 2 * defaultTextIconGap;
268:
269: Insets insets = b.getInsets();
270: if (insets != null) {
271: r.width += insets.left + insets.right;
272: r.height += insets.top + insets.bottom;
273: }
274:
275: // if the width is even, bump it up one. This is critical
276: // for the focus dash line to draw properly
277: /* JGoodies: Can't believe the above
278: * if(r.width%2 == 0) {
279: r.width++;
280: }*/
281:
282: // if the height is even, bump it up one. This is critical
283: // for the text to center properly
284: // JGoodies: An even height is critical to center icons properly
285: if (r.height % 2 == 1) {
286: r.height++;
287: }
288: return r.getSize();
289: }
290:
291: public void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
292: Icon arrowIcon, Color background, Color foreground,
293: int defaultTextIconGap) {
294: JMenuItem b = (JMenuItem) c;
295: ButtonModel model = b.getModel();
296:
297: // Dimension size = b.getSize();
298: int menuWidth = b.getWidth();
299: int menuHeight = b.getHeight();
300: Insets i = c.getInsets();
301:
302: resetRects();
303:
304: viewRect.setBounds(0, 0, menuWidth, menuHeight);
305:
306: viewRect.x += i.left;
307: viewRect.y += i.top;
308: viewRect.width -= (i.right + viewRect.x);
309: viewRect.height -= (i.bottom + viewRect.y);
310:
311: Font holdf = g.getFont();
312: Font f = c.getFont();
313: g.setFont(f);
314: FontMetrics fm = g.getFontMetrics(f);
315: FontMetrics fmAccel = g.getFontMetrics(acceleratorFont);
316:
317: // get Accelerator text
318: KeyStroke accelerator = b.getAccelerator();
319: String acceleratorText = "";
320: if (accelerator != null) {
321: int modifiers = accelerator.getModifiers();
322: if (modifiers > 0) {
323: acceleratorText = KeyEvent
324: .getKeyModifiersText(modifiers);
325: acceleratorText += acceleratorDelimiter;
326: }
327:
328: int keyCode = accelerator.getKeyCode();
329: if (keyCode != 0) {
330: acceleratorText += KeyEvent.getKeyText(keyCode);
331: } else {
332: acceleratorText += accelerator.getKeyChar();
333: }
334: }
335:
336: Icon wrappedIcon = getWrappedIcon(getIcon(menuItem, checkIcon));
337: Icon wrappedArrowIcon = new MinimumSizedIcon(arrowIcon);
338:
339: // layout the text and icon
340: String text = layoutMenuItem(fm, b.getText(),
341: fmAccel,
342: acceleratorText,
343: // b.getIcon(), checkIcon,
344: null,
345: wrappedIcon,
346: wrappedArrowIcon, //arrowIcon,
347: b.getVerticalAlignment(), b.getHorizontalAlignment(), b
348: .getVerticalTextPosition(), b
349: .getHorizontalTextPosition(), viewRect,
350: iconRect, textRect, acceleratorRect, checkIconRect,
351: arrowIconRect, b.getText() == null ? 0
352: : defaultTextIconGap, defaultTextIconGap);
353:
354: // Paint background
355: paintBackground(g, b, background);
356:
357: // Paint icon
358: Color holdc = g.getColor();
359: if (model.isArmed()
360: || (c instanceof JMenu && model.isSelected())) {
361: g.setColor(foreground);
362: }
363: wrappedIcon.paintIcon(c, g, checkIconRect.x, checkIconRect.y);
364: g.setColor(holdc);
365:
366: // Draw the Text
367: if (text != null) {
368: View v = (View) c.getClientProperty(HTML_KEY);
369: if (v != null) {
370: v.paint(g, textRect);
371: } else {
372: paintText(g, b, textRect, text);
373: }
374: }
375:
376: // Draw the Accelerator Text
377: if (acceleratorText != null && !acceleratorText.equals("")) {
378:
379: //Get the maxAccWidth from the parent to calculate the offset.
380: int accOffset = 0;
381: Container parent = menuItem.getParent();
382: if (parent != null && parent instanceof JComponent) {
383: JComponent p = (JComponent) parent;
384: Integer maxValueInt = (Integer) p
385: .getClientProperty(MAX_ACC_WIDTH);
386: int maxValue = maxValueInt != null ? maxValueInt
387: .intValue() : acceleratorRect.width;
388:
389: //Calculate the offset, with which the accelerator texts will be drawn with.
390: accOffset = isLeftToRight(menuItem) ? maxValue
391: - acceleratorRect.width : acceleratorRect.width
392: - maxValue;
393: }
394:
395: g.setFont(acceleratorFont);
396: if (!model.isEnabled()) {
397: // *** paint the acceleratorText disabled
398: if (!disabledTextHasShadow()) {
399: g.setColor(disabledForeground);
400: RenderingUtils.drawStringUnderlineCharAt(c, g,
401: acceleratorText, -1, acceleratorRect.x
402: - accOffset, acceleratorRect.y
403: + fmAccel.getAscent());
404: } else {
405: g.setColor(b.getBackground().brighter());
406: RenderingUtils.drawStringUnderlineCharAt(c, g,
407: acceleratorText, -1, acceleratorRect.x
408: - accOffset, acceleratorRect.y
409: + fmAccel.getAscent());
410: g.setColor(b.getBackground().darker());
411: RenderingUtils.drawStringUnderlineCharAt(c, g,
412: acceleratorText, -1, acceleratorRect.x
413: - accOffset - 1, acceleratorRect.y
414: + fmAccel.getAscent() - 1);
415: }
416: } else {
417: // *** paint the acceleratorText normally
418: if (model.isArmed()
419: || (c instanceof JMenu && model.isSelected())) {
420: g.setColor(acceleratorSelectionForeground);
421: } else {
422: g.setColor(acceleratorForeground);
423: }
424: RenderingUtils.drawStringUnderlineCharAt(c, g,
425: acceleratorText, -1, acceleratorRect.x
426: - accOffset, acceleratorRect.y
427: + fmAccel.getAscent());
428: }
429: }
430:
431: // Paint the Arrow
432: if (arrowIcon != null) {
433: if (model.isArmed()
434: || (c instanceof JMenu && model.isSelected()))
435: g.setColor(foreground);
436: if (useCheckAndArrow())
437: wrappedArrowIcon.paintIcon(c, g, arrowIconRect.x,
438: arrowIconRect.y);
439: }
440: g.setColor(holdc);
441: g.setFont(holdf);
442: }
443:
444: /**
445: * Compute and return the location of the icons origin, the
446: * location of origin of the text baseline, and a possibly clipped
447: * version of the compound labels string. Locations are computed
448: * relative to the viewRect rectangle.
449: */
450: private String layoutMenuItem(FontMetrics fm, String text,
451: FontMetrics fmAccel, String acceleratorText, Icon icon,
452: Icon checkIcon, Icon arrowIcon, int verticalAlignment,
453: int horizontalAlignment, int verticalTextPosition,
454: int horizontalTextPosition, Rectangle viewRectangle,
455: Rectangle iconRectangle, Rectangle textRectangle,
456: Rectangle acceleratorRectangle,
457: Rectangle checkIconRectangle, Rectangle arrowIconRectangle,
458: int textIconGap, int menuItemGap) {
459:
460: SwingUtilities.layoutCompoundLabel(menuItem, fm, text, icon,
461: verticalAlignment, horizontalAlignment,
462: verticalTextPosition, horizontalTextPosition,
463: viewRectangle, iconRectangle, textRectangle,
464: textIconGap);
465:
466: /* Initialize the acceleratorText bounds rectangle textRect. If a null
467: * or and empty String was specified we substitute "" here
468: * and use 0,0,0,0 for acceleratorTextRect.
469: */
470: if ((acceleratorText == null) || acceleratorText.equals("")) {
471: acceleratorRectangle.width = acceleratorRectangle.height = 0;
472: acceleratorText = "";
473: } else {
474: acceleratorRectangle.width = SwingUtilities
475: .computeStringWidth(fmAccel, acceleratorText);
476: acceleratorRectangle.height = fmAccel.getHeight();
477: }
478:
479: boolean useCheckAndArrow = useCheckAndArrow();
480:
481: // Initialize the checkIcon bounds rectangle's width & height.
482:
483: if (useCheckAndArrow) {
484: if (checkIcon != null) {
485: checkIconRectangle.width = checkIcon.getIconWidth();
486: checkIconRectangle.height = checkIcon.getIconHeight();
487: } else {
488: checkIconRectangle.width = checkIconRectangle.height = 0;
489: }
490:
491: // Initialize the arrowIcon bounds rectangle width & height.
492:
493: if (arrowIcon != null) {
494: arrowIconRectangle.width = arrowIcon.getIconWidth();
495: arrowIconRectangle.height = arrowIcon.getIconHeight();
496: } else {
497: arrowIconRectangle.width = arrowIconRectangle.height = 0;
498: }
499: }
500:
501: Rectangle labelRect = iconRectangle.union(textRectangle);
502: if (isLeftToRight(menuItem)) {
503: textRectangle.x += menuItemGap;
504: iconRectangle.x += menuItemGap;
505:
506: // Position the Accelerator text rect
507: acceleratorRectangle.x = viewRectangle.x
508: + viewRectangle.width - arrowIconRectangle.width
509: - menuItemGap - acceleratorRectangle.width;
510:
511: // Position the Check and Arrow Icons
512: if (useCheckAndArrow) {
513: checkIconRectangle.x = viewRectangle.x; // + menuItemGap; JGoodies: No leading gap
514: textRectangle.x += menuItemGap
515: + checkIconRectangle.width;
516: iconRectangle.x += menuItemGap
517: + checkIconRectangle.width;
518: arrowIconRectangle.x = viewRectangle.x
519: + viewRectangle.width - menuItemGap
520: - arrowIconRectangle.width;
521: }
522: } else {
523: textRectangle.x -= menuItemGap;
524: iconRectangle.x -= menuItemGap;
525:
526: // Position the Accelerator text rect
527: acceleratorRectangle.x = viewRectangle.x
528: + arrowIconRectangle.width + menuItemGap;
529:
530: // Position the Check and Arrow Icons
531: if (useCheckAndArrow) {
532: // JGoodies: No trailing gap
533: checkIconRectangle.x = viewRectangle.x
534: + viewRectangle.width
535: - checkIconRectangle.width;
536: textRectangle.x -= menuItemGap
537: + checkIconRectangle.width;
538: iconRectangle.x -= menuItemGap
539: + checkIconRectangle.width;
540: arrowIconRectangle.x = viewRectangle.x + menuItemGap;
541: }
542: }
543:
544: // Align the accelerator text and the check and arrow icons vertically
545: // with the center of the label rect.
546: acceleratorRectangle.y = labelRect.y + (labelRect.height / 2)
547: - (acceleratorRectangle.height / 2);
548: if (useCheckAndArrow) {
549: arrowIconRectangle.y = labelRect.y + (labelRect.height / 2)
550: - (arrowIconRectangle.height / 2);
551: checkIconRectangle.y = labelRect.y + (labelRect.height / 2)
552: - (checkIconRectangle.height / 2);
553: }
554:
555: /*
556: System.out.println("Layout: text="+menuItem.getText()+"\n\tv="
557: +viewRect+"\n\tc="+checkIconRect+"\n\ti="
558: +iconRect+"\n\tt="+textRect+"\n\tacc="
559: +acceleratorRect+"\n\ta="+arrowIconRect+"\n");
560: */
561:
562: return text;
563: }
564:
565: /*
566: * Returns false if the component is a JMenu and it is a top
567: * level menu (on the menubar).
568: */
569: private boolean useCheckAndArrow() {
570: boolean isTopLevelMenu = menuItem instanceof JMenu
571: && ((JMenu) menuItem).isTopLevelMenu();
572: return !isTopLevelMenu;
573: }
574:
575: private boolean isLeftToRight(Component c) {
576: return c.getComponentOrientation().isLeftToRight();
577: }
578:
579: // Copies from 1.4.1 ****************************************************
580:
581: /**
582: * Draws the background of the menu item.
583: * Copied from 1.4.1 BasicMenuItem to make it visible to the
584: * MenuItemLayouter
585: *
586: * @param g the paint graphics
587: * @param aMenuItem menu item to be painted
588: * @param bgColor selection background color
589: * @since 1.4
590: */
591: private void paintBackground(Graphics g, JMenuItem aMenuItem,
592: Color bgColor) {
593: ButtonModel model = aMenuItem.getModel();
594:
595: if (aMenuItem.isOpaque()) {
596: int menuWidth = aMenuItem.getWidth();
597: int menuHeight = aMenuItem.getHeight();
598: Color c = model.isArmed()
599: || (aMenuItem instanceof JMenu && model
600: .isSelected()) ? bgColor : aMenuItem
601: .getBackground();
602: Color oldColor = g.getColor();
603: g.setColor(c);
604: g.fillRect(0, 0, menuWidth, menuHeight);
605: g.setColor(oldColor);
606: }
607: }
608:
609: /**
610: * Renders the text of the current menu item.
611: * <p>
612: * @param g graphics context
613: * @param aMenuItem menu item to render
614: * @param textRectangle bounding rectangle for rendering the text
615: * @param text string to render
616: * @since 1.4
617: */
618: private void paintText(Graphics g, JMenuItem aMenuItem,
619: Rectangle textRectangle, String text) {
620: ButtonModel model = aMenuItem.getModel();
621: FontMetrics fm = g.getFontMetrics();
622: int mnemIndex = aMenuItem.getDisplayedMnemonicIndex();
623: if (isMnemonicHidden()) {
624: mnemIndex = -1;
625: }
626:
627: if (!model.isEnabled()) {
628: if (!disabledTextHasShadow()) {
629: // *** paint the text disabled
630: g.setColor(UIManager
631: .getColor("MenuItem.disabledForeground"));
632: RenderingUtils.drawStringUnderlineCharAt(aMenuItem, g,
633: text, mnemIndex, textRectangle.x,
634: textRectangle.y + fm.getAscent());
635: } else {
636: // *** paint the text disabled with a shadow
637: g.setColor(aMenuItem.getBackground().brighter());
638: RenderingUtils.drawStringUnderlineCharAt(aMenuItem, g,
639: text, mnemIndex, textRectangle.x,
640: textRectangle.y + fm.getAscent());
641: g.setColor(aMenuItem.getBackground().darker());
642: RenderingUtils.drawStringUnderlineCharAt(aMenuItem, g,
643: text, mnemIndex, textRectangle.x - 1,
644: textRectangle.y + fm.getAscent() - 1);
645: }
646: } else {
647: // *** paint the text normally
648: if (model.isArmed()
649: || (aMenuItem instanceof JMenu && model
650: .isSelected())) {
651: g.setColor(selectionForeground); // Uses protected field.
652: }
653: RenderingUtils.drawStringUnderlineCharAt(aMenuItem, g,
654: text, mnemIndex, textRectangle.x, textRectangle.y
655: + fm.getAscent());
656: }
657: }
658:
659: protected boolean isMnemonicHidden() {
660: return false;
661: }
662:
663: protected boolean disabledTextHasShadow() {
664: return false;
665: }
666:
667: /**
668: * Checks and answers if the parent menu indicates that we should use no icons.
669: */
670: private boolean hideIcons() {
671: Component parent = menuItem.getParent();
672: if (!(parent instanceof JPopupMenu)) {
673: return false;
674: }
675: JPopupMenu popupMenu = (JPopupMenu) parent;
676: Object value = popupMenu
677: .getClientProperty(Options.NO_ICONS_KEY);
678: if (value == null) {
679: Component invoker = popupMenu.getInvoker();
680: if (invoker != null && invoker instanceof JMenu)
681: value = ((JMenu) invoker)
682: .getClientProperty(Options.NO_ICONS_KEY);
683: }
684: return Boolean.TRUE.equals(value);
685: }
686:
687: /**
688: * Used as a placeholder if icons are disabled.
689: */
690: private static class NullIcon implements Icon {
691: public int getIconWidth() {
692: return 0;
693: }
694:
695: public int getIconHeight() {
696: return 0;
697: }
698:
699: public void paintIcon(Component c, Graphics g, int x, int y) {
700: // The NullIcon doesn't paint anything.
701: }
702: }
703:
704: }
|