0001: /*
0002: * @(#)MetalMenuItemUI.java
0003: *
0004: * Copyright 2002 - 2004 JIDE Software Inc. All rights reserved.
0005: */
0006:
0007: package com.jidesoft.plaf.metal;
0008:
0009: import com.jidesoft.plaf.UIDefaultsLookup;
0010: import com.jidesoft.plaf.basic.ThemePainter;
0011: import com.jidesoft.swing.JideSwingUtilities;
0012: import com.jidesoft.swing.TopLevelMenuContainer;
0013:
0014: import javax.swing.*;
0015: import javax.swing.event.*;
0016: import javax.swing.plaf.*;
0017: import javax.swing.plaf.basic.BasicGraphicsUtils;
0018: import javax.swing.plaf.basic.BasicHTML;
0019: import javax.swing.text.View;
0020: import java.awt.*;
0021: import java.awt.event.ActionEvent;
0022: import java.awt.event.InputEvent;
0023: import java.awt.event.KeyEvent;
0024: import java.awt.event.MouseEvent;
0025: import java.awt.geom.AffineTransform;
0026: import java.beans.PropertyChangeEvent;
0027: import java.beans.PropertyChangeListener;
0028:
0029: /**
0030: * MetalMenuItem implementation
0031: */
0032: public class MetalMenuItemUI extends MenuItemUI {
0033: protected JMenuItem menuItem = null;
0034: protected Color selectionBackground;
0035: protected Color selectionForeground;
0036: protected Color disabledForeground;
0037: protected Color acceleratorForeground;
0038: protected Color acceleratorSelectionForeground;
0039: private String acceleratorDelimiter;
0040:
0041: protected int defaultTextIconGap;
0042: protected Font acceleratorFont;
0043:
0044: protected MouseInputListener mouseInputListener;
0045: protected MenuDragMouseListener menuDragMouseListener;
0046: protected MenuKeyListener menuKeyListener;
0047: private PropertyChangeListener propertyChangeListener;
0048:
0049: protected Icon arrowIcon = null;
0050: protected Icon checkIcon = null;
0051:
0052: protected boolean oldBorderPainted;
0053:
0054: /**
0055: * Used for accelerator binding, lazily created.
0056: */
0057: InputMap windowInputMap;
0058:
0059: /* diagnostic aids -- should be false for production builds. */
0060: private static final boolean TRACE = false; // trace creates and disposes
0061:
0062: private static final boolean VERBOSE = false; // show reuse hits/misses
0063: private static final boolean DEBUG = false; // show bad params, misc.
0064:
0065: /* Client Property keys for text and accelerator text widths */
0066: static final String MAX_TEXT_WIDTH = "maxTextWidth";
0067: static final String MAX_ACC_WIDTH = "maxAccWidth";
0068:
0069: // PORTING: add 1
0070: protected ThemePainter _painter;
0071:
0072: public static ComponentUI createUI(JComponent c) {
0073: return new MetalMenuItemUI();
0074: }
0075:
0076: @Override
0077: public void installUI(JComponent c) {
0078: menuItem = (JMenuItem) c;
0079:
0080: installDefaults();
0081: installComponents(menuItem);
0082: installListeners();
0083: installKeyboardActions();
0084: }
0085:
0086: protected void installDefaults() {
0087: // PORTING: add 1
0088: _painter = (ThemePainter) UIDefaultsLookup.get("Theme.painter");
0089:
0090: String prefix = getPropertyPrefix();
0091:
0092: acceleratorFont = UIDefaultsLookup
0093: .getFont("MenuItem.acceleratorFont");
0094:
0095: menuItem.setOpaque(true);
0096: if (menuItem.getMargin() == null
0097: || (menuItem.getMargin() instanceof UIResource)) {
0098: menuItem.setMargin(UIDefaultsLookup.getInsets(prefix
0099: + ".margin"));
0100: }
0101:
0102: defaultTextIconGap = 4; // Should be from table
0103:
0104: LookAndFeel.installBorder(menuItem, prefix + ".border");
0105: oldBorderPainted = menuItem.isBorderPainted();
0106: menuItem.setBorderPainted((Boolean) (UIDefaultsLookup
0107: .get(prefix + ".borderPainted")));
0108: LookAndFeel.installColorsAndFont(menuItem, prefix
0109: + ".background", prefix + ".foreground", prefix
0110: + ".font");
0111:
0112: // MenuItem specific defaults
0113: if (selectionBackground == null
0114: || selectionBackground instanceof UIResource) {
0115: selectionBackground = UIDefaultsLookup.getColor(prefix
0116: + ".selectionBackground");
0117: }
0118: if (selectionForeground == null
0119: || selectionForeground instanceof UIResource) {
0120: selectionForeground = UIDefaultsLookup.getColor(prefix
0121: + ".selectionForeground");
0122: }
0123: if (disabledForeground == null
0124: || disabledForeground instanceof UIResource) {
0125: disabledForeground = UIDefaultsLookup.getColor(prefix
0126: + ".disabledForeground");
0127: }
0128: if (acceleratorForeground == null
0129: || acceleratorForeground instanceof UIResource) {
0130: acceleratorForeground = UIDefaultsLookup.getColor(prefix
0131: + ".acceleratorForeground");
0132: }
0133: if (acceleratorSelectionForeground == null
0134: || acceleratorSelectionForeground instanceof UIResource) {
0135: acceleratorSelectionForeground = UIDefaultsLookup
0136: .getColor(prefix
0137: + ".acceleratorSelectionForeground");
0138: }
0139: // Get accelerator delimiter
0140: acceleratorDelimiter = UIDefaultsLookup
0141: .getString("MenuItem.acceleratorDelimiter");
0142: if (acceleratorDelimiter == null) {
0143: acceleratorDelimiter = "+";
0144: }
0145: // Icons
0146: if (arrowIcon == null || arrowIcon instanceof UIResource) {
0147: arrowIcon = UIDefaultsLookup.getIcon(prefix + ".arrowIcon");
0148: }
0149: if (checkIcon == null || checkIcon instanceof UIResource) {
0150: checkIcon = UIDefaultsLookup.getIcon(prefix + ".checkIcon");
0151: }
0152: }
0153:
0154: /**
0155: * @since 1.3
0156: */
0157: protected void installComponents(JMenuItem menuItem) {
0158: BasicHTML.updateRenderer(menuItem, menuItem.getText());
0159: }
0160:
0161: protected String getPropertyPrefix() {
0162: return "MenuItem";
0163: }
0164:
0165: protected void installListeners() {
0166: if ((mouseInputListener = createMouseInputListener(menuItem)) != null) {
0167: menuItem.addMouseListener(mouseInputListener);
0168: menuItem.addMouseMotionListener(mouseInputListener);
0169: }
0170: if ((menuDragMouseListener = createMenuDragMouseListener(menuItem)) != null) {
0171: menuItem.addMenuDragMouseListener(menuDragMouseListener);
0172: }
0173: if ((menuKeyListener = createMenuKeyListener(menuItem)) != null) {
0174: menuItem.addMenuKeyListener(menuKeyListener);
0175: }
0176: if ((propertyChangeListener = createPropertyChangeListener(menuItem)) != null) {
0177: menuItem.addPropertyChangeListener(propertyChangeListener);
0178: }
0179: }
0180:
0181: protected void installKeyboardActions() {
0182: ActionMap actionMap = getActionMap();
0183:
0184: SwingUtilities.replaceUIActionMap(menuItem, actionMap);
0185: updateAcceleratorBinding();
0186: }
0187:
0188: @Override
0189: public void uninstallUI(JComponent c) {
0190: menuItem = (JMenuItem) c;
0191: uninstallDefaults();
0192: uninstallComponents(menuItem);
0193: uninstallListeners();
0194: uninstallKeyboardActions();
0195:
0196: //Remove the textWidth and accWidth values from the parent's Client Properties.
0197: Container parent = menuItem.getParent();
0198: if ((parent != null && parent instanceof JComponent)
0199: && !(menuItem instanceof JMenu && ((JMenu) menuItem)
0200: .isTopLevelMenu())) {
0201: JComponent p = (JComponent) parent;
0202: p.putClientProperty(MetalMenuItemUI.MAX_ACC_WIDTH, null);
0203: p.putClientProperty(MetalMenuItemUI.MAX_TEXT_WIDTH, null);
0204: }
0205:
0206: menuItem = null;
0207: }
0208:
0209: protected void uninstallDefaults() {
0210: // PORTING: add 1
0211: _painter = null;
0212:
0213: LookAndFeel.uninstallBorder(menuItem);
0214: menuItem.setBorderPainted(oldBorderPainted);
0215: if (menuItem.getMargin() instanceof UIResource)
0216: menuItem.setMargin(null);
0217: if (arrowIcon instanceof UIResource)
0218: arrowIcon = null;
0219: if (checkIcon instanceof UIResource)
0220: checkIcon = null;
0221: }
0222:
0223: /**
0224: * @since 1.3
0225: */
0226: protected void uninstallComponents(JMenuItem menuItem) {
0227: BasicHTML.updateRenderer(menuItem, "");
0228: }
0229:
0230: protected void uninstallListeners() {
0231: if (mouseInputListener != null) {
0232: menuItem.removeMouseListener(mouseInputListener);
0233: menuItem.removeMouseMotionListener(mouseInputListener);
0234: }
0235: if (menuDragMouseListener != null) {
0236: menuItem.removeMenuDragMouseListener(menuDragMouseListener);
0237: }
0238: if (menuKeyListener != null) {
0239: menuItem.removeMenuKeyListener(menuKeyListener);
0240: }
0241: if (propertyChangeListener != null) {
0242: menuItem
0243: .removePropertyChangeListener(propertyChangeListener);
0244: }
0245:
0246: mouseInputListener = null;
0247: menuDragMouseListener = null;
0248: menuKeyListener = null;
0249: propertyChangeListener = null;
0250: }
0251:
0252: protected void uninstallKeyboardActions() {
0253: SwingUtilities.replaceUIActionMap(menuItem, null);
0254: if (windowInputMap != null) {
0255: SwingUtilities.replaceUIInputMap(menuItem,
0256: JComponent.WHEN_IN_FOCUSED_WINDOW, null);
0257: windowInputMap = null;
0258: }
0259: }
0260:
0261: protected MouseInputListener createMouseInputListener(JComponent c) {
0262: return new MouseInputHandler();
0263: }
0264:
0265: protected MenuDragMouseListener createMenuDragMouseListener(
0266: JComponent c) {
0267: return new MenuDragMouseHandler();
0268: }
0269:
0270: protected MenuKeyListener createMenuKeyListener(JComponent c) {
0271: return new MenuKeyHandler();
0272: }
0273:
0274: private PropertyChangeListener createPropertyChangeListener(
0275: JComponent c) {
0276: return new PropertyChangeHandler();
0277: }
0278:
0279: ActionMap getActionMap() {
0280: String propertyPrefix = getPropertyPrefix();
0281: String uiKey = propertyPrefix + ".actionMap";
0282: ActionMap am = (ActionMap) UIDefaultsLookup.get(uiKey);
0283: if (am == null) {
0284: am = createActionMap();
0285: UIManager.getLookAndFeelDefaults().put(uiKey, am);
0286: }
0287: return am;
0288: }
0289:
0290: ActionMap createActionMap() {
0291: ActionMap map = new ActionMapUIResource();
0292: map.put("doClick", new ClickAction());
0293: return map;
0294: }
0295:
0296: InputMap createInputMap(int condition) {
0297: if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) {
0298: return new ComponentInputMapUIResource(menuItem);
0299: }
0300: return null;
0301: }
0302:
0303: void updateAcceleratorBinding() {
0304: KeyStroke accelerator = menuItem.getAccelerator();
0305:
0306: if (windowInputMap != null) {
0307: windowInputMap.clear();
0308: }
0309: if (accelerator != null) {
0310: if (windowInputMap == null) {
0311: windowInputMap = createInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
0312: SwingUtilities.replaceUIInputMap(menuItem,
0313: JComponent.WHEN_IN_FOCUSED_WINDOW,
0314: windowInputMap);
0315: }
0316: windowInputMap.put(accelerator, "doClick");
0317: }
0318: }
0319:
0320: @Override
0321: public Dimension getMinimumSize(JComponent c) {
0322: Dimension d = null;
0323: View v = (View) c.getClientProperty(BasicHTML.propertyKey);
0324: if (v != null) {
0325: d = getPreferredSize(c);
0326: d.width -= v.getPreferredSpan(View.X_AXIS)
0327: - v.getMinimumSpan(View.X_AXIS);
0328: }
0329: return d;
0330: }
0331:
0332: @Override
0333: public Dimension getPreferredSize(JComponent c) {
0334: return getPreferredMenuItemSize(c, checkIcon, arrowIcon,
0335: defaultTextIconGap);
0336: }
0337:
0338: @Override
0339: public Dimension getMaximumSize(JComponent c) {
0340: Dimension d = null;
0341: View v = (View) c.getClientProperty(BasicHTML.propertyKey);
0342: if (v != null) {
0343: d = getPreferredSize(c);
0344: d.width += v.getMaximumSpan(View.X_AXIS)
0345: - v.getPreferredSpan(View.X_AXIS);
0346: }
0347: return d;
0348: }
0349:
0350: // these rects are used for painting and preferredsize calculations.
0351: // they used to be regenerated constantly. Now they are reused.
0352: static Rectangle zeroRect = new Rectangle(0, 0, 0, 0);
0353: static Rectangle iconRect = new Rectangle();
0354: static Rectangle textRect = new Rectangle();
0355: static Rectangle acceleratorRect = new Rectangle();
0356: static Rectangle checkIconRect = new Rectangle();
0357: static Rectangle arrowIconRect = new Rectangle();
0358: static Rectangle viewRect = new Rectangle(Short.MAX_VALUE,
0359: Short.MAX_VALUE);
0360: static Rectangle r = new Rectangle();
0361:
0362: private void resetRects() {
0363: iconRect.setBounds(zeroRect);
0364: textRect.setBounds(zeroRect);
0365: acceleratorRect.setBounds(zeroRect);
0366: checkIconRect.setBounds(zeroRect);
0367: arrowIconRect.setBounds(zeroRect);
0368: viewRect.setBounds(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
0369: r.setBounds(zeroRect);
0370: }
0371:
0372: protected Dimension getPreferredMenuItemSize(JComponent c,
0373: Icon checkIcon, Icon arrowIcon, int defaultTextIconGap) {
0374: JMenuItem b = (JMenuItem) c;
0375: Icon icon = (Icon) b.getIcon();
0376: String text = b.getText();
0377: KeyStroke accelerator = b.getAccelerator();
0378: String acceleratorText = "";
0379:
0380: if (accelerator != null) {
0381: int modifiers = accelerator.getModifiers();
0382: if (modifiers > 0) {
0383: acceleratorText = KeyEvent
0384: .getKeyModifiersText(modifiers);
0385: //acceleratorText += "-";
0386: acceleratorText += acceleratorDelimiter;
0387: }
0388: int keyCode = accelerator.getKeyCode();
0389: if (keyCode != 0) {
0390: acceleratorText += KeyEvent.getKeyText(keyCode);
0391: } else {
0392: acceleratorText += accelerator.getKeyChar();
0393: }
0394: }
0395:
0396: Font font = b.getFont();
0397: FontMetrics fm = b.getFontMetrics(font);
0398: FontMetrics fmAccel = b.getFontMetrics(acceleratorFont);
0399:
0400: resetRects();
0401:
0402: layoutMenuItem(fm, text, fmAccel, acceleratorText, icon,
0403: checkIcon, arrowIcon, b.getVerticalAlignment(), b
0404: .getHorizontalAlignment(), b
0405: .getVerticalTextPosition(), b
0406: .getHorizontalTextPosition(), viewRect,
0407: iconRect, textRect, acceleratorRect, checkIconRect,
0408: arrowIconRect, text == null ? 0 : defaultTextIconGap,
0409: defaultTextIconGap);
0410: // find the union of the icon and text rects
0411: r.setBounds(textRect);
0412: r = SwingUtilities.computeUnion(iconRect.x, iconRect.y,
0413: iconRect.width, iconRect.height, r);
0414: // r = iconRect.union(textRect);
0415:
0416: // To make the accelerator texts appear in a column, find the widest MenuItem text
0417: // and the widest accelerator text.
0418:
0419: //Get the parent, which stores the information.
0420: Container parent = menuItem.getParent();
0421:
0422: //Check the parent, and see that it is not a top-level menu.
0423: if (parent != null
0424: && parent instanceof JComponent
0425: && !(menuItem instanceof JMenu && ((JMenu) menuItem)
0426: .isTopLevelMenu())) {
0427: JComponent p = (JComponent) parent;
0428:
0429: //Get widest text so far from parent, if no one exists null is returned.
0430: Integer maxTextWidth = (Integer) p
0431: .getClientProperty(MetalMenuItemUI.MAX_TEXT_WIDTH);
0432: Integer maxAccWidth = (Integer) p
0433: .getClientProperty(MetalMenuItemUI.MAX_ACC_WIDTH);
0434:
0435: int maxTextValue = maxTextWidth != null ? maxTextWidth : 0;
0436: int maxAccValue = maxAccWidth != null ? maxAccWidth : 0;
0437:
0438: //Compare the text widths, and adjust the r.width to the widest.
0439: if (r.width < maxTextValue) {
0440: r.width = maxTextValue;
0441: } else {
0442: p.putClientProperty(MetalMenuItemUI.MAX_TEXT_WIDTH,
0443: r.width);
0444: }
0445:
0446: //Compare the accelarator widths.
0447: if (acceleratorRect.width > maxAccValue) {
0448: maxAccValue = acceleratorRect.width;
0449: p.putClientProperty(MetalMenuItemUI.MAX_ACC_WIDTH,
0450: acceleratorRect.width);
0451: }
0452:
0453: //Add on the widest accelerator
0454: r.width += maxAccValue;
0455: r.width += defaultTextIconGap;
0456:
0457: }
0458:
0459: if (useCheckAndArrow()) {
0460: // Add in the checkIcon
0461: r.width += checkIconRect.width;
0462: r.width += defaultTextIconGap;
0463:
0464: // Add in the arrowIcon
0465: r.width += defaultTextIconGap;
0466: r.width += arrowIconRect.width;
0467: }
0468:
0469: r.width += 2 * defaultTextIconGap;
0470:
0471: Insets insets = b.getInsets();
0472: if (insets != null) {
0473: r.width += insets.left + insets.right;
0474: r.height += insets.top + insets.bottom;
0475: }
0476:
0477: // if the width is even, bump it up one. This is critical
0478: // for the focus dash line to draw properly
0479: if (r.width % 2 == 0) {
0480: r.width++;
0481: }
0482:
0483: // if the height is even, bump it up one. This is critical
0484: // for the text to center properly
0485: if (r.height % 2 == 0) {
0486: r.height++;
0487: }
0488: /*
0489: if(!(b instanceof JMenu && ((JMenu) b).isTopLevelMenu()) ) {
0490:
0491: // Container parent = menuItem.getParent();
0492: JComponent p = (JComponent) parent;
0493:
0494: System.out.println("MaxText: "+p.getClientProperty(BasicMenuItemUI.MAX_TEXT_WIDTH));
0495: System.out.println("MaxACC"+p.getClientProperty(BasicMenuItemUI.MAX_ACC_WIDTH));
0496:
0497: System.out.println("returning pref.width: " + r.width);
0498: System.out.println("Current getSize: " + b.getSize() + "\n");
0499: }*/
0500: if (JideSwingUtilities.getOrientationOf(menuItem) == SwingConstants.HORIZONTAL) {
0501: return r.getSize();
0502: } else {
0503: return new Dimension(r.height, r.width);
0504: }
0505: }
0506:
0507: /**
0508: * We draw the background in paintMenuItem()
0509: * so override update (which fills the background of opaque
0510: * components by default) to just call paint().
0511: */
0512: @Override
0513: public void update(Graphics g, JComponent c) {
0514: paint(g, c);
0515: }
0516:
0517: @Override
0518: public void paint(Graphics g, JComponent c) {
0519: paintMenuItem(g, c, checkIcon, arrowIcon, selectionBackground,
0520: selectionForeground, defaultTextIconGap);
0521: }
0522:
0523: protected void paintMenuItem(Graphics g, JComponent c,
0524: Icon checkIcon, Icon arrowIcon, Color background,
0525: Color foreground, int defaultTextIconGap) {
0526: JMenuItem b = (JMenuItem) c;
0527: ButtonModel model = b.getModel();
0528:
0529: int menuWidth = 0;
0530: int menuHeight = 0;
0531:
0532: if (JideSwingUtilities.getOrientationOf(menuItem) == SwingConstants.HORIZONTAL) {
0533: // Dimension size = b.getSize();
0534: menuWidth = b.getWidth();
0535: menuHeight = b.getHeight();
0536: } else {
0537: // Dimension size = b.getSize();
0538: menuWidth = b.getHeight();
0539: menuHeight = b.getWidth();
0540: Graphics2D g2d = (Graphics2D) g;
0541: AffineTransform oldAt = g2d.getTransform();
0542: g2d.rotate(Math.PI / 2);
0543: g2d.translate(0, -menuHeight + 1);
0544: }
0545:
0546: Insets i = c.getInsets();
0547:
0548: resetRects();
0549:
0550: viewRect.setBounds(0, 0, menuWidth, menuHeight);
0551:
0552: viewRect.x += i.left;
0553: viewRect.y += i.top;
0554: viewRect.width -= (i.right + viewRect.x);
0555: viewRect.height -= (i.bottom + viewRect.y);
0556:
0557: Font holdf = g.getFont();
0558: Font f = c.getFont();
0559: g.setFont(f);
0560: FontMetrics fm = g.getFontMetrics(f);
0561: FontMetrics fmAccel = g.getFontMetrics(acceleratorFont);
0562:
0563: // get Accelerator text
0564: KeyStroke accelerator = b.getAccelerator();
0565: String acceleratorText = "";
0566: if (accelerator != null) {
0567: int modifiers = accelerator.getModifiers();
0568: if (modifiers > 0) {
0569: acceleratorText = KeyEvent
0570: .getKeyModifiersText(modifiers);
0571: //acceleratorText += "-";
0572: acceleratorText += acceleratorDelimiter;
0573: }
0574:
0575: int keyCode = accelerator.getKeyCode();
0576: if (keyCode != 0) {
0577: acceleratorText += KeyEvent.getKeyText(keyCode);
0578: } else {
0579: acceleratorText += accelerator.getKeyChar();
0580: }
0581: }
0582:
0583: // layout the text and icon
0584: String text = layoutMenuItem(fm, b.getText(), fmAccel,
0585: acceleratorText, b.getIcon(), checkIcon, arrowIcon, b
0586: .getVerticalAlignment(), b
0587: .getHorizontalAlignment(), b
0588: .getVerticalTextPosition(), b
0589: .getHorizontalTextPosition(), viewRect,
0590: iconRect, textRect, acceleratorRect, checkIconRect,
0591: arrowIconRect, b.getText() == null ? 0
0592: : defaultTextIconGap, defaultTextIconGap);
0593:
0594: // Paint background
0595: paintBackground(g, b, background);
0596:
0597: Color holdc = g.getColor();
0598:
0599: // Paint the Check
0600: if (checkIcon != null) {
0601: if (model.isArmed()
0602: || (c instanceof JMenu && model.isSelected())) {
0603: g.setColor(foreground);
0604: } else {
0605: g.setColor(holdc);
0606: }
0607: if (useCheckAndArrow())
0608: checkIcon.paintIcon(c, g, checkIconRect.x,
0609: checkIconRect.y);
0610: g.setColor(holdc);
0611: }
0612:
0613: // Paint the Icon
0614: if (b.getIcon() != null) {
0615: Icon icon;
0616: if (!model.isEnabled()) {
0617: icon = (Icon) b.getDisabledIcon();
0618: } else if (model.isPressed() && model.isArmed()) {
0619: icon = (Icon) b.getPressedIcon();
0620: if (icon == null) {
0621: // Use default icon
0622: icon = (Icon) b.getIcon();
0623: }
0624: } else {
0625: icon = (Icon) b.getIcon();
0626: }
0627:
0628: if (icon != null)
0629: icon.paintIcon(c, g, iconRect.x, iconRect.y);
0630: }
0631:
0632: // Draw the Text
0633: if (text != null) {
0634: View v = (View) c.getClientProperty(BasicHTML.propertyKey);
0635: if (v != null) {
0636: v.paint(g, textRect);
0637: } else {
0638: paintText(g, b, textRect, text);
0639: }
0640: }
0641:
0642: // Draw the Accelerator Text
0643: if (acceleratorText != null && !acceleratorText.equals("")) {
0644:
0645: //Get the maxAccWidth from the parent to calculate the offset.
0646: int accOffset = 0;
0647: Container parent = menuItem.getParent();
0648: if (parent != null && parent instanceof JComponent) {
0649: JComponent p = (JComponent) parent;
0650: Integer maxValueInt = (Integer) p
0651: .getClientProperty(MetalMenuItemUI.MAX_ACC_WIDTH);
0652: int maxValue = maxValueInt != null ? maxValueInt
0653: : acceleratorRect.width;
0654:
0655: //Calculate the offset, with which the accelerator texts will be drawn with.
0656: accOffset = maxValue - acceleratorRect.width;
0657: }
0658:
0659: g.setFont(acceleratorFont);
0660: if (!model.isEnabled()) {
0661: // *** paint the acceleratorText disabled
0662: if (disabledForeground != null) {
0663: g.setColor(disabledForeground);
0664: JideSwingUtilities.drawString(menuItem, g,
0665: acceleratorText, acceleratorRect.x
0666: - accOffset, acceleratorRect.y
0667: + fmAccel.getAscent());
0668: } else {
0669: g.setColor(b.getBackground().brighter());
0670: JideSwingUtilities.drawString(menuItem, g,
0671: acceleratorText, acceleratorRect.x
0672: - accOffset, acceleratorRect.y
0673: + fmAccel.getAscent());
0674: g.setColor(b.getBackground().darker());
0675: JideSwingUtilities.drawString(menuItem, g,
0676: acceleratorText, acceleratorRect.x
0677: - accOffset - 1, acceleratorRect.y
0678: + fmAccel.getAscent() - 1);
0679: }
0680: } else {
0681: // *** paint the acceleratorText normally
0682: if (model.isArmed()
0683: || (c instanceof JMenu && model.isSelected())) {
0684: g.setColor(acceleratorSelectionForeground);
0685: } else {
0686: g.setColor(acceleratorForeground);
0687: }
0688: JideSwingUtilities.drawString(menuItem, g,
0689: acceleratorText, acceleratorRect.x - accOffset,
0690: acceleratorRect.y + fmAccel.getAscent());
0691: }
0692: }
0693:
0694: // Paint the Arrow
0695: if (arrowIcon != null) {
0696: if (model.isArmed()
0697: || (c instanceof JMenu && model.isSelected()))
0698: g.setColor(foreground);
0699: if (useCheckAndArrow())
0700: arrowIcon.paintIcon(c, g, arrowIconRect.x,
0701: arrowIconRect.y);
0702: }
0703: g.setColor(holdc);
0704: g.setFont(holdf);
0705: }
0706:
0707: /**
0708: * Draws the background of the menu item.
0709: *
0710: * @param g the paint graphics
0711: * @param menuItem menu item to be painted
0712: * @param bgColor selection background color
0713: * @since 1.4
0714: */
0715: protected void paintBackground(Graphics g, JMenuItem menuItem,
0716: Color bgColor) {
0717: ButtonModel model = menuItem.getModel();
0718: Color oldColor = g.getColor();
0719: int menuWidth = menuItem.getWidth();
0720: int menuHeight = menuItem.getHeight();
0721:
0722: if (menuItem.isOpaque()) {
0723: if (model.isArmed()
0724: || (menuItem instanceof JMenu && model.isSelected())) {
0725: g.setColor(bgColor);
0726: g.fillRect(0, 0, menuWidth, menuHeight);
0727: } else {
0728: g.setColor(menuItem.getBackground());
0729: g.fillRect(0, 0, menuWidth, menuHeight);
0730: }
0731: g.setColor(oldColor);
0732: }
0733: }
0734:
0735: /**
0736: * Renders the text of the current menu item.
0737: * <p/>
0738: *
0739: * @param g graphics context
0740: * @param menuItem menu item to render
0741: * @param textRect bounding rectangle for rendering the text
0742: * @param text string to render
0743: * @since 1.4
0744: */
0745: protected void paintText(Graphics g, JMenuItem menuItem,
0746: Rectangle textRect, String text) {
0747: ButtonModel model = menuItem.getModel();
0748: FontMetrics fm = g.getFontMetrics();
0749: int mnemIndex = menuItem.getDisplayedMnemonicIndex();
0750:
0751: if (!model.isEnabled()) {
0752: // *** paint the text disabled
0753: if (UIDefaultsLookup.get("MenuItem.disabledForeground") instanceof Color) {
0754: g.setColor(UIDefaultsLookup
0755: .getColor("MenuItem.disabledForeground"));
0756: BasicGraphicsUtils.drawStringUnderlineCharAt(g, text,
0757: mnemIndex, textRect.x, textRect.y
0758: + fm.getAscent());
0759: } else {
0760: g.setColor(menuItem.getBackground().brighter());
0761: BasicGraphicsUtils.drawStringUnderlineCharAt(g, text,
0762: mnemIndex, textRect.x, textRect.y
0763: + fm.getAscent());
0764: g.setColor(menuItem.getBackground().darker());
0765: BasicGraphicsUtils.drawStringUnderlineCharAt(g, text,
0766: mnemIndex, textRect.x - 1, textRect.y
0767: + fm.getAscent() - 1);
0768: }
0769: } else {
0770: // *** paint the text normally
0771: if (model.isArmed()
0772: || (menuItem instanceof JMenu && model.isSelected())) {
0773: g.setColor(selectionForeground); // Uses protected field.
0774: }
0775: BasicGraphicsUtils.drawStringUnderlineCharAt(g, text,
0776: mnemIndex, textRect.x, textRect.y + fm.getAscent());
0777: }
0778: }
0779:
0780: /**
0781: * Compute and return the location of the icons origin, the
0782: * location of origin of the text baseline, and a possibly clipped
0783: * version of the compound labels string. Locations are computed
0784: * relative to the viewRect rectangle.
0785: */
0786:
0787: private String layoutMenuItem(FontMetrics fm, String text,
0788: FontMetrics fmAccel, String acceleratorText, Icon icon,
0789: Icon checkIcon, Icon arrowIcon, int verticalAlignment,
0790: int horizontalAlignment, int verticalTextPosition,
0791: int horizontalTextPosition, Rectangle viewRect,
0792: Rectangle iconRect, Rectangle textRect,
0793: Rectangle acceleratorRect, Rectangle checkIconRect,
0794: Rectangle arrowIconRect, int textIconGap, int menuItemGap) {
0795:
0796: SwingUtilities.layoutCompoundLabel(menuItem, fm, text, icon,
0797: verticalAlignment, horizontalAlignment,
0798: verticalTextPosition, horizontalTextPosition, viewRect,
0799: iconRect, textRect, textIconGap);
0800:
0801: /* Initialize the acceelratorText bounds rectangle textRect. If a null
0802: * or and empty String was specified we substitute "" here
0803: * and use 0,0,0,0 for acceleratorTextRect.
0804: */
0805: if ((acceleratorText == null) || acceleratorText.equals("")) {
0806: acceleratorRect.width = acceleratorRect.height = 0;
0807: acceleratorText = "";
0808: } else {
0809: acceleratorRect.width = SwingUtilities.computeStringWidth(
0810: fmAccel, acceleratorText);
0811: acceleratorRect.height = fmAccel.getHeight();
0812: }
0813:
0814: /* Initialize the checkIcon bounds rectangle's width & height.
0815: */
0816:
0817: if (useCheckAndArrow()) {
0818: if (checkIcon != null) {
0819: checkIconRect.width = checkIcon.getIconWidth();
0820: checkIconRect.height = checkIcon.getIconHeight();
0821: } else {
0822: checkIconRect.width = checkIconRect.height = 0;
0823: }
0824:
0825: /* Initialize the arrowIcon bounds rectangle width & height.
0826: */
0827:
0828: if (arrowIcon != null) {
0829: arrowIconRect.width = arrowIcon.getIconWidth();
0830: arrowIconRect.height = arrowIcon.getIconHeight();
0831: } else {
0832: arrowIconRect.width = arrowIconRect.height = 0;
0833: }
0834: }
0835:
0836: Rectangle labelRect = iconRect.union(textRect);
0837: if (menuItem.getComponentOrientation().isLeftToRight()) {
0838: textRect.x += menuItemGap;
0839: iconRect.x += menuItemGap;
0840:
0841: // Position the Accelerator text rect
0842: acceleratorRect.x = viewRect.x + viewRect.width
0843: - arrowIconRect.width - menuItemGap
0844: - acceleratorRect.width;
0845:
0846: // Position the Check and Arrow Icons
0847: if (useCheckAndArrow()) {
0848: checkIconRect.x = viewRect.x + menuItemGap;
0849: textRect.x += menuItemGap + checkIconRect.width;
0850: iconRect.x += menuItemGap + checkIconRect.width;
0851: arrowIconRect.x = viewRect.x + viewRect.width
0852: - menuItemGap - arrowIconRect.width;
0853: }
0854: } else {
0855: textRect.x -= menuItemGap;
0856: iconRect.x -= menuItemGap;
0857:
0858: // Position the Accelerator text rect
0859: acceleratorRect.x = viewRect.x + arrowIconRect.width
0860: + menuItemGap;
0861:
0862: // Position the Check and Arrow Icons
0863: if (useCheckAndArrow()) {
0864: checkIconRect.x = viewRect.x + viewRect.width
0865: - menuItemGap - checkIconRect.width;
0866: textRect.x -= menuItemGap + checkIconRect.width;
0867: iconRect.x -= menuItemGap + checkIconRect.width;
0868: arrowIconRect.x = viewRect.x + menuItemGap;
0869: }
0870: }
0871:
0872: // Align the accelertor text and the check and arrow icons vertically
0873: // with the center of the label rect.
0874: acceleratorRect.y = labelRect.y + (labelRect.height / 2)
0875: - (acceleratorRect.height / 2);
0876: if (useCheckAndArrow()) {
0877: arrowIconRect.y = labelRect.y + (labelRect.height / 2)
0878: - (arrowIconRect.height / 2);
0879: checkIconRect.y = labelRect.y + (labelRect.height / 2)
0880: - (checkIconRect.height / 2);
0881: }
0882:
0883: /*
0884: System.out.println("Layout: text="+menuItem.getText()+"\n\tv="
0885: +viewRect+"\n\tc="+checkIconRect+"\n\ti="
0886: +iconRect+"\n\tt="+textRect+"\n\tacc="
0887: +acceleratorRect+"\n\ta="+arrowIconRect+"\n");
0888: */
0889:
0890: return text;
0891: }
0892:
0893: /*
0894: * Returns false if the component is a JMenu and it is a top
0895: * level menu (on the menubar).
0896: */
0897: private boolean useCheckAndArrow() {
0898: boolean b = true;
0899: if ((menuItem instanceof JMenu)
0900: && (((JMenu) menuItem).isTopLevelMenu())) {
0901: b = false;
0902: }
0903: return b;
0904: }
0905:
0906: public MenuElement[] getPath() {
0907: MenuSelectionManager m = MenuSelectionManager.defaultManager();
0908: MenuElement oldPath[] = m.getSelectedPath();
0909: MenuElement newPath[];
0910: int i = oldPath.length;
0911: if (i == 0)
0912: return new MenuElement[0];
0913: Component parent = menuItem.getParent();
0914: if (oldPath[i - 1].getComponent() == parent) {
0915: // The parent popup menu is the last so far
0916: newPath = new MenuElement[i + 1];
0917: System.arraycopy(oldPath, 0, newPath, 0, i);
0918: newPath[i] = menuItem;
0919: } else {
0920: // A sibling menuitem is the current selection
0921: //
0922: // This probably needs to handle 'exit submenu into
0923: // a menu item. Search backwards along the current
0924: // selection until you find the parent popup menu,
0925: // then copy up to that and add yourself...
0926: int j;
0927: for (j = oldPath.length - 1; j >= 0; j--) {
0928: if (oldPath[j].getComponent() == parent)
0929: break;
0930: }
0931: newPath = new MenuElement[j + 2];
0932: System.arraycopy(oldPath, 0, newPath, 0, j + 1);
0933: newPath[j + 1] = menuItem;
0934: /*
0935: System.out.println("Sibling condition -- ");
0936: System.out.println("Old array : ");
0937: printMenuElementArray(oldPath, false);
0938: System.out.println("New array : ");
0939: printMenuElementArray(newPath, false);
0940: */
0941: }
0942: return newPath;
0943: }
0944:
0945: void printMenuElementArray(MenuElement path[], boolean dumpStack) {
0946: System.out.println("Path is(");
0947: int i, j;
0948: for (i = 0, j = path.length; i < j; i++) {
0949: for (int k = 0; k <= i; k++)
0950: System.out.print(" ");
0951: MenuElement me = (MenuElement) path[i];
0952: if (me instanceof JMenuItem)
0953: System.out.println(((JMenuItem) me).getText() + ", ");
0954: else if (me == null)
0955: System.out.println("NULL , ");
0956: else
0957: System.out.println("" + me + ", ");
0958: }
0959: System.out.println(")");
0960:
0961: if (dumpStack == true)
0962: Thread.dumpStack();
0963: }
0964:
0965: protected class MouseInputHandler implements MouseInputListener {
0966: public void mouseClicked(MouseEvent e) {
0967: }
0968:
0969: public void mousePressed(MouseEvent e) {
0970: }
0971:
0972: public void mouseReleased(MouseEvent e) {
0973: MenuSelectionManager manager = MenuSelectionManager
0974: .defaultManager();
0975: Point p = e.getPoint();
0976: if (p.x >= 0 && p.x < menuItem.getWidth() && p.y >= 0
0977: && p.y < menuItem.getHeight()) {
0978: doClick(manager);
0979: } else {
0980: manager.processMouseEvent(e);
0981: }
0982: }
0983:
0984: public void mouseEntered(MouseEvent e) {
0985: MenuSelectionManager manager = MenuSelectionManager
0986: .defaultManager();
0987: int modifiers = e.getModifiers();
0988: // 4188027: drag enter/exit added in JDK 1.1.7A, JDK1.2
0989: if ((modifiers & (InputEvent.BUTTON1_MASK
0990: | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) != 0) {
0991: MenuSelectionManager.defaultManager()
0992: .processMouseEvent(e);
0993: } else {
0994: manager.setSelectedPath(getPath());
0995: }
0996: }
0997:
0998: public void mouseExited(MouseEvent e) {
0999: MenuSelectionManager manager = MenuSelectionManager
1000: .defaultManager();
1001:
1002: int modifiers = e.getModifiers();
1003: // 4188027: drag enter/exit added in JDK 1.1.7A, JDK1.2
1004: if ((modifiers & (InputEvent.BUTTON1_MASK
1005: | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) != 0) {
1006: MenuSelectionManager.defaultManager()
1007: .processMouseEvent(e);
1008: } else {
1009:
1010: MenuElement path[] = manager.getSelectedPath();
1011: if (path.length > 1) {
1012: MenuElement newPath[] = new MenuElement[path.length - 1];
1013: int i, c;
1014: for (i = 0, c = path.length - 1; i < c; i++)
1015: newPath[i] = path[i];
1016: manager.setSelectedPath(newPath);
1017: }
1018: }
1019: }
1020:
1021: public void mouseDragged(MouseEvent e) {
1022: MenuSelectionManager.defaultManager().processMouseEvent(e);
1023: }
1024:
1025: public void mouseMoved(MouseEvent e) {
1026: }
1027: }
1028:
1029: private class MenuDragMouseHandler implements MenuDragMouseListener {
1030: public void menuDragMouseEntered(MenuDragMouseEvent e) {
1031: MenuSelectionManager manager = e.getMenuSelectionManager();
1032: MenuElement path[] = e.getPath();
1033: manager.setSelectedPath(path);
1034: }
1035:
1036: public void menuDragMouseDragged(MenuDragMouseEvent e) {
1037: MenuSelectionManager manager = e.getMenuSelectionManager();
1038: MenuElement path[] = e.getPath();
1039: manager.setSelectedPath(path);
1040: }
1041:
1042: public void menuDragMouseExited(MenuDragMouseEvent e) {
1043: }
1044:
1045: public void menuDragMouseReleased(MenuDragMouseEvent e) {
1046: MenuSelectionManager manager = e.getMenuSelectionManager();
1047: MenuElement path[] = e.getPath();
1048: Point p = e.getPoint();
1049: if (p.x >= 0 && p.x < menuItem.getWidth() && p.y >= 0
1050: && p.y < menuItem.getHeight()) {
1051: doClick(manager);
1052: } else {
1053: manager.clearSelectedPath();
1054: }
1055: }
1056: }
1057:
1058: private class MenuKeyHandler implements MenuKeyListener {
1059:
1060: /**
1061: * Handles the mnemonic key typed in the MenuItem if this menuItem is in
1062: * a standalone popup menu. This invocation normally
1063: * handled in BasicMenuUI.MenuKeyHandler.menuKeyPressed. Ideally, the
1064: * MenuKeyHandlers for both BasicMenuItemUI and BasicMenuUI can be consolidated
1065: * into BasicPopupMenuUI but that would require an semantic change. This
1066: * would result in a performance win since we can shortcut a lot of the needless
1067: * processing from MenuSelectionManager.processKeyEvent(). See 4670831.
1068: */
1069: public void menuKeyTyped(MenuKeyEvent e) {
1070: if (DEBUG) {
1071: System.out
1072: .println("in BasicMenuItemUI.menuKeyTyped for "
1073: + menuItem.getText());
1074: }
1075: int key = menuItem.getMnemonic();
1076: if (key == 0 || e.getPath().length != 2) // Hack! Only proceed if in a JPopupMenu
1077: return;
1078: if (lower((char) key) == lower(e.getKeyChar())) {
1079: MenuSelectionManager manager = e
1080: .getMenuSelectionManager();
1081: doClick(manager);
1082: e.consume();
1083: }
1084: }
1085:
1086: public void menuKeyPressed(MenuKeyEvent e) {
1087: if (DEBUG) {
1088: System.out
1089: .println("in BasicMenuItemUI.menuKeyPressed for "
1090: + menuItem.getText());
1091: }
1092: }
1093:
1094: public void menuKeyReleased(MenuKeyEvent e) {
1095: }
1096:
1097: private char lower(char keyChar) {
1098: return Character.toLowerCase(keyChar);
1099: }
1100: }
1101:
1102: private class PropertyChangeHandler implements
1103: PropertyChangeListener {
1104: public void propertyChange(PropertyChangeEvent e) {
1105: String name = e.getPropertyName();
1106:
1107: if (name.equals("labelFor")
1108: || name.equals("displayedMnemonic")
1109: || name.equals("accelerator")) {
1110: updateAcceleratorBinding();
1111: } else if (name.equals("text") || "font".equals(name)
1112: || "foreground".equals(name)) {
1113: // remove the old html view client property if one
1114: // existed, and install a new one if the text installed
1115: // into the JLabel is html source.
1116: JMenuItem lbl = ((JMenuItem) e.getSource());
1117: String text = lbl.getText();
1118: BasicHTML.updateRenderer(lbl, text);
1119: }
1120: }
1121: }
1122:
1123: private static class ClickAction extends AbstractAction {
1124: public void actionPerformed(ActionEvent e) {
1125: JMenuItem mi = (JMenuItem) e.getSource();
1126: MenuSelectionManager.defaultManager().clearSelectedPath();
1127: mi.doClick();
1128: }
1129: }
1130:
1131: /**
1132: * Call this method when a menu item is to be activated.
1133: * This method handles some of the details of menu item activation
1134: * such as clearing the selected path and messaging the
1135: * JMenuItem's doClick() method.
1136: *
1137: * @param msm A MenuSelectionManager. The visual feedback and
1138: * internal bookkeeping tasks are delegated to
1139: * this MenuSelectionManager. If <code>null</code> is
1140: * passed as this argument, the
1141: * <code>MenuSelectionManager.defaultManager</code> is
1142: * used.
1143: * @see javax.swing.MenuSelectionManager
1144: * @see javax.swing.JMenuItem#doClick(int)
1145: * @since 1.4
1146: */
1147: protected void doClick(MenuSelectionManager msm) {
1148: // Visual feedback
1149: if (msm == null) {
1150: msm = MenuSelectionManager.defaultManager();
1151: }
1152: msm.clearSelectedPath();
1153: menuItem.doClick(0);
1154: }
1155:
1156: /**
1157: * This is to see if the menu item in question is part of the
1158: * system menu on an internal frame.
1159: * The Strings that are being checked can be found in
1160: * MetalInternalFrameTitlePaneUI.java,
1161: * WindowsInternalFrameTitlePaneUI.java, and
1162: * MotifInternalFrameTitlePaneUI.java.
1163: *
1164: * @since 1.4
1165: */
1166: private boolean isInternalFrameSystemMenu() {
1167: String actionCommand = menuItem.getActionCommand();
1168: if ((actionCommand.equals("Close"))
1169: || (actionCommand.equals("Minimize"))
1170: || (actionCommand.equals("Restore"))
1171: || (actionCommand.equals("Maximize"))) {
1172: return true;
1173: } else {
1174: return false;
1175: }
1176: }
1177:
1178: // PORTING: add 3
1179: public ThemePainter getPainter() {
1180: return _painter;
1181: }
1182:
1183: protected boolean isDownArrowVisible(Container c) {
1184: if (c instanceof TopLevelMenuContainer
1185: && ((TopLevelMenuContainer) c).isMenuBar()) {
1186: return false;
1187: } else if (c instanceof TopLevelMenuContainer
1188: && !((TopLevelMenuContainer) c).isMenuBar()) {
1189: return true;
1190: } else if (c instanceof JMenuBar) {
1191: return false;
1192: } else {
1193: return true;
1194: }
1195: }
1196: }
|