001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Alexander T. Simbirtsev
019: * @version $Revision$
020: */package javax.swing.plaf.basic;
021:
022: import java.awt.Color;
023: import java.awt.Dimension;
024: import java.awt.Font;
025: import java.awt.FontMetrics;
026: import java.awt.Graphics;
027: import java.awt.Rectangle;
028: import java.awt.event.ActionEvent;
029: import java.awt.event.FocusEvent;
030: import java.awt.event.FocusListener;
031: import java.awt.event.MouseEvent;
032: import java.beans.PropertyChangeEvent;
033: import java.beans.PropertyChangeListener;
034:
035: import javax.swing.AbstractAction;
036: import javax.swing.Action;
037: import javax.swing.ActionMap;
038: import javax.swing.Icon;
039: import javax.swing.JComponent;
040: import javax.swing.JMenuItem;
041: import javax.swing.KeyStroke;
042: import javax.swing.LookAndFeel;
043: import javax.swing.MenuElement;
044: import javax.swing.MenuSelectionManager;
045: import javax.swing.SwingUtilities;
046: import javax.swing.Timer;
047: import javax.swing.UIManager;
048: import javax.swing.event.ChangeEvent;
049: import javax.swing.event.ChangeListener;
050: import javax.swing.event.MenuDragMouseEvent;
051: import javax.swing.event.MenuDragMouseListener;
052: import javax.swing.event.MenuKeyListener;
053: import javax.swing.event.MouseInputListener;
054: import javax.swing.plaf.ActionMapUIResource;
055: import javax.swing.plaf.ComponentUI;
056: import javax.swing.plaf.MenuItemUI;
057:
058: import org.apache.harmony.x.swing.ButtonCommons;
059: import org.apache.harmony.x.swing.StringConstants;
060: import org.apache.harmony.x.swing.Utilities;
061:
062: import org.apache.harmony.x.swing.internal.nls.Messages;
063:
064: public class BasicMenuItemUI extends MenuItemUI {
065: protected class MouseInputHandler implements MouseInputListener {
066: public void mouseEntered(final MouseEvent e) {
067: final MenuSelectionManager manager = MenuSelectionManager
068: .defaultManager();
069: if (openMenuTimer != null) {
070: openMenuTimer.stop();
071: }
072: manager.setSelectedPath(Utilities.addToPath(manager
073: .getSelectedPath(), menuItem));
074: }
075:
076: public void mouseExited(final MouseEvent e) {
077: final MenuSelectionManager manager = MenuSelectionManager
078: .defaultManager();
079: manager.setSelectedPath(Utilities.removeFromPath(manager
080: .getSelectedPath(), menuItem));
081: }
082:
083: public void mouseReleased(final MouseEvent e) {
084: MenuSelectionManager defaultManager = MenuSelectionManager
085: .defaultManager();
086: if (defaultManager.componentForPoint(e.getComponent(), e
087: .getPoint()) == menuItem
088: || Utilities.isEmptyArray(defaultManager
089: .getSelectedPath())) {
090:
091: doClick(defaultManager);
092: } else {
093: defaultManager.processMouseEvent(e);
094: }
095: }
096:
097: public void mousePressed(final MouseEvent e) {
098: MenuSelectionManager.defaultManager().processMouseEvent(e);
099: }
100:
101: public void mouseClicked(final MouseEvent e) {
102: MenuSelectionManager.defaultManager().processMouseEvent(e);
103: }
104:
105: public void mouseDragged(final MouseEvent e) {
106: MenuSelectionManager.defaultManager().processMouseEvent(e);
107: }
108:
109: public void mouseMoved(final MouseEvent e) {
110: MenuSelectionManager.defaultManager().processMouseEvent(e);
111: }
112: }
113:
114: private class MenuItemHandler implements MenuDragMouseListener,
115: FocusListener, ChangeListener, PropertyChangeListener {
116:
117: public void stateChanged(final ChangeEvent e) {
118: menuItem.revalidate();
119: menuItem.repaint();
120: }
121:
122: public void propertyChange(final PropertyChangeEvent event) {
123: menuItem.revalidate();
124: menuItem.repaint();
125: }
126:
127: public void menuDragMouseReleased(final MenuDragMouseEvent e) {
128: doClick(MenuSelectionManager.defaultManager());
129: }
130:
131: public void menuDragMouseDragged(final MenuDragMouseEvent e) {
132: }
133:
134: public void menuDragMouseEntered(final MenuDragMouseEvent e) {
135: }
136:
137: public void menuDragMouseExited(final MenuDragMouseEvent e) {
138: }
139:
140: public void focusGained(final FocusEvent e) {
141: }
142:
143: public void focusLost(final FocusEvent e) {
144: }
145: }
146:
147: protected JMenuItem menuItem;
148:
149: protected Icon arrowIcon;
150: protected Icon checkIcon;
151:
152: protected Color selectionBackground;
153: protected Color selectionForeground;
154: protected Color disabledForeground;
155: protected Color acceleratorForeground;
156: protected Color acceleratorSelectionForeground;
157: protected int defaultTextIconGap;
158: protected Font acceleratorFont;
159:
160: protected boolean oldBorderPainted;
161:
162: protected MouseInputListener mouseInputListener;
163: protected MenuDragMouseListener menuDragMouseListener;
164: protected MenuKeyListener menuKeyListener;
165:
166: private MenuItemHandler menuItemHandler;
167:
168: private static final String PROPERTY_PREFIX = "MenuItem";
169:
170: private final Rectangle viewR = new Rectangle();
171: private final Rectangle iconR = new Rectangle();
172: private final Rectangle textR = new Rectangle();
173:
174: private static final Action MNEMONIC_ACTION = new AbstractAction() {
175: public void actionPerformed(final ActionEvent e) {
176: MenuSelectionManager.defaultManager().clearSelectedPath();
177: final JMenuItem item = (JMenuItem) e.getSource();
178: item.doClick(0);
179: }
180: };
181:
182: private String acceleratorDelimiter = "";
183:
184: static Timer openMenuTimer;
185:
186: public static ComponentUI createUI(final JComponent c) {
187: return new BasicMenuItemUI();
188: }
189:
190: protected String getPropertyPrefix() {
191: return PROPERTY_PREFIX;
192: }
193:
194: public void installUI(final JComponent c) {
195: menuItem = (JMenuItem) c;
196: installDefaults();
197: installComponents(menuItem);
198: installListeners();
199: installKeyboardActions();
200: }
201:
202: public void uninstallUI(final JComponent c) {
203: // Fix for HARMONY-2704, for compatibility with RI
204: JMenuItem jMenuItem = (JMenuItem) c;
205:
206: uninstallKeyboardActions();
207: uninstallListeners();
208: uninstallComponents(menuItem);
209: uninstallDefaults();
210: menuItem = null;
211: }
212:
213: protected void installDefaults() {
214: LookAndFeel.installColorsAndFont(menuItem, getPropertyPrefix()
215: + ".background", getPropertyPrefix() + ".foreground",
216: getPropertyPrefix() + ".font");
217: LookAndFeel.installBorder(menuItem, getPropertyPrefix()
218: + ".border");
219: menuItem.setMargin(UIManager.getInsets(getPropertyPrefix()
220: + ".margin"));
221:
222: LookAndFeel.installProperty(menuItem, "opaque", Boolean.TRUE);
223: arrowIcon = UIManager.getIcon(getPropertyPrefix()
224: + ".arrowIcon");
225: checkIcon = UIManager.getIcon(getPropertyPrefix()
226: + ".checkIcon");
227:
228: selectionBackground = UIManager.getColor(getPropertyPrefix()
229: + ".selectionBackground");
230: selectionForeground = UIManager.getColor(getPropertyPrefix()
231: + ".selectionForeground");
232: disabledForeground = UIManager.getColor(getPropertyPrefix()
233: + ".disabledForeground");
234: acceleratorForeground = UIManager.getColor(getPropertyPrefix()
235: + ".acceleratorForeground");
236: acceleratorSelectionForeground = UIManager
237: .getColor(getPropertyPrefix()
238: + ".acceleratorSelectionForeground");
239:
240: acceleratorFont = UIManager.getFont(getPropertyPrefix()
241: + ".acceleratorFont");
242: String delim = UIManager.getString(getPropertyPrefix()
243: + ".acceleratorDelimiter");
244: acceleratorDelimiter = (delim != null) ? delim : "+";
245:
246: defaultTextIconGap = 4;
247: oldBorderPainted = UIManager.getBoolean(getPropertyPrefix()
248: + ".borderPainted");
249: }
250:
251: protected void uninstallDefaults() {
252: LookAndFeel.uninstallBorder(menuItem);
253: Utilities.uninstallColorsAndFont(menuItem);
254: if (Utilities.isUIResource(menuItem.getMargin())) {
255: menuItem.setMargin(null);
256: }
257: }
258:
259: protected void installComponents(final JMenuItem menuItem) {
260: }
261:
262: protected void uninstallComponents(final JMenuItem menuItem) {
263: }
264:
265: protected void installListeners() {
266: if (menuItem == null) {
267: return;
268: }
269:
270: menuDragMouseListener = createMenuDragMouseListener(menuItem);
271: menuKeyListener = createMenuKeyListener(menuItem);
272: mouseInputListener = createMouseInputListener(menuItem);
273: menuItem.addMenuDragMouseListener(menuDragMouseListener);
274: menuItem.addMenuKeyListener(menuKeyListener);
275: menuItem.addMouseListener(mouseInputListener);
276: menuItem.addMouseMotionListener(mouseInputListener);
277:
278: menuItemHandler = new MenuItemHandler();
279: menuItem.addFocusListener(menuItemHandler);
280: menuItem.addPropertyChangeListener(menuItemHandler);
281: menuItem.addChangeListener(menuItemHandler);
282: }
283:
284: protected void uninstallListeners() {
285: if (menuItem == null) {
286: return;
287: }
288:
289: menuItem.removeMenuDragMouseListener(menuDragMouseListener);
290: menuItem.removeMenuKeyListener(menuKeyListener);
291: menuItem.removeMouseListener(mouseInputListener);
292: menuItem.removeMouseMotionListener(mouseInputListener);
293: menuItem.removePropertyChangeListener(menuItemHandler);
294: menuItem.removeChangeListener(menuItemHandler);
295: menuItem.removeFocusListener(menuItemHandler);
296:
297: menuItemHandler = null;
298: menuDragMouseListener = null;
299: menuKeyListener = null;
300: mouseInputListener = null;
301: }
302:
303: protected void installKeyboardActions() {
304: ActionMap actionMap = new ActionMapUIResource();
305: actionMap.put(StringConstants.MNEMONIC_ACTION, MNEMONIC_ACTION);
306: actionMap.setParent(((BasicLookAndFeel) UIManager
307: .getLookAndFeel()).getAudioActionMap());
308: SwingUtilities.replaceUIActionMap(menuItem, actionMap);
309: }
310:
311: protected void uninstallKeyboardActions() {
312: SwingUtilities.replaceUIActionMap(menuItem, null);
313: }
314:
315: protected MouseInputListener createMouseInputListener(
316: final JComponent c) {
317: return new MouseInputHandler();
318: }
319:
320: protected MenuDragMouseListener createMenuDragMouseListener(
321: final JComponent c) {
322: return (menuItemHandler != null) ? menuItemHandler
323: : (menuItemHandler = new MenuItemHandler());
324: }
325:
326: protected MenuKeyListener createMenuKeyListener(final JComponent c) {
327: return null;
328: }
329:
330: public Dimension getMinimumSize(final JComponent c) {
331: if (c == null) {
332: throw new NullPointerException(Messages.getString(
333: "swing.03", "component")); //$NON-NLS-1$ //$NON-NLS-2$
334: }
335: return null;
336: }
337:
338: public Dimension getPreferredSize(final JComponent c) {
339: return getPreferredMenuItemSize(c, checkIcon, arrowIcon,
340: defaultTextIconGap);
341: }
342:
343: public Dimension getMaximumSize(final JComponent c) {
344: if (c == null) {
345: throw new NullPointerException(Messages.getString(
346: "swing.03", "component")); //$NON-NLS-1$ //$NON-NLS-2$
347: }
348: return null;
349: }
350:
351: protected Dimension getPreferredMenuItemSize(final JComponent c,
352: final Icon check, final Icon arrow, final int textIconGap) {
353:
354: final JMenuItem item = (JMenuItem) c;
355: Dimension result = ButtonCommons.getPreferredSize(item, null,
356: textIconGap);
357: if (arrow != null && isArrowToBeDrawn()) {
358: result.width += arrow.getIconWidth() + textIconGap;
359: result.height = Math.max(result.height, arrow
360: .getIconHeight());
361: }
362: if (check != null) {
363: result.width += check.getIconWidth() + textIconGap;
364: result.height = Math.max(result.height, check
365: .getIconHeight());
366: }
367: final KeyStroke accelerator = item.getAccelerator();
368: if (accelerator != null) {
369: String accelText = Utilities.getAcceleratorText(
370: accelerator, acceleratorDelimiter);
371: FontMetrics fm = item.getFontMetrics(acceleratorFont);
372: result.width += fm.stringWidth(accelText) + 3 * textIconGap;
373: }
374:
375: result.width += 1 + 2 * textIconGap;
376: result.height += 1;
377:
378: return result;
379: }
380:
381: public void update(final Graphics g, final JComponent c) {
382: paint(g, c);
383: }
384:
385: public void paint(final Graphics g, final JComponent c) {
386: final JMenuItem item = (JMenuItem) c;
387: Color bgColor = (isPaintArmed() && item.isEnabled()) ? selectionBackground
388: : item.getBackground();
389: Color fgColor = !item.isEnabled() ? disabledForeground
390: : (!item.isArmed() ? item.getForeground()
391: : selectionForeground);
392: paintMenuItem(g, c, checkIcon, arrowIcon, bgColor, fgColor,
393: defaultTextIconGap);
394: }
395:
396: protected void paintMenuItem(final Graphics g, final JComponent c,
397: final Icon check, final Icon arrow, final Color background,
398: final Color foreground, final int textIconGap) {
399: final Color oldColor = g.getColor();
400: final JMenuItem item = (JMenuItem) c;
401: Icon icon = item.getIcon();
402: final boolean isLTR = item.getComponentOrientation()
403: .isLeftToRight();
404: final int arrowInset = getArrowInset(arrow, textIconGap, item);
405: final int checkInset = getCheckInset(check, textIconGap, item);
406: final int leftInset = isLTR ? checkInset : arrowInset;
407: final int rightInset = isLTR ? arrowInset : checkInset;
408:
409: viewR.setBounds(0, 0, 0, 0);
410: ButtonCommons.getPaintingParameters(item, viewR, iconR, textR,
411: icon, leftInset, rightInset);
412:
413: paintBackground(g, item, background);
414: if (arrow != null && isArrowToBeDrawn()) {
415: int arrowX = viewR.x + textIconGap
416: + (isLTR ? viewR.width : -arrowInset);
417: int arrowY = viewR.y
418: + (viewR.height - arrow.getIconHeight()) / 2;
419: arrow.paintIcon(c, g, arrowX, arrowY);
420: }
421: if (check != null) {
422: int checkX = viewR.x + textIconGap
423: + (isLTR ? -checkInset : viewR.width);
424: int checkY = viewR.y
425: + (viewR.height - check.getIconHeight()) / 2;
426: check.paintIcon(c, g, checkX, checkY);
427: }
428:
429: icon = ButtonCommons.getCurrentIcon(item);
430: if (icon != null) {
431: icon.paintIcon(c, g, iconR.x, iconR.y);
432: }
433: paintText(g, item, textR, item.getText());
434: paintAccelerator(g, viewR, iconR, textR, textIconGap, isLTR);
435: g.setColor(oldColor);
436: }
437:
438: private int getCheckInset(final Icon check, final int textIconGap,
439: final JMenuItem item) {
440: int checkInset;
441: if (check != null) {
442: checkInset = check.getIconWidth() + 2 * textIconGap;
443: } else {
444: checkInset = !Utilities.isEmptyString(item.getText()) ? textIconGap
445: : 0;
446: }
447: return checkInset;
448: }
449:
450: private int getArrowInset(final Icon arrow, final int textIconGap,
451: final JMenuItem item) {
452: int arrowInset;
453: if (arrow != null && isArrowToBeDrawn()) {
454: arrowInset = arrow.getIconWidth() + 2 * textIconGap;
455: } else {
456: arrowInset = !Utilities.isEmptyString(item.getText()) ? textIconGap
457: : 0;
458: }
459: return arrowInset;
460: }
461:
462: private void paintAccelerator(final Graphics g,
463: final Rectangle viewR, final Rectangle iconR,
464: final Rectangle textR, final int textIconGap,
465: final boolean isLTR) {
466: final KeyStroke accel = menuItem.getAccelerator();
467: if (accel == null
468: || viewR.width <= iconR.width + textR.width + 3
469: * textIconGap) {
470: return;
471: }
472:
473: final String acceleratorText = Utilities.getAcceleratorText(
474: accel, acceleratorDelimiter);
475: final FontMetrics fm = menuItem.getFontMetrics(acceleratorFont);
476: final int acceleratorWidth = fm.stringWidth(acceleratorText);
477: if (viewR.width < iconR.width + textR.width + 3 * textIconGap
478: + acceleratorWidth) {
479: return;
480: }
481:
482: final Color color = menuItem.isEnabled() ? (menuItem.isArmed() ? acceleratorSelectionForeground
483: : acceleratorForeground)
484: : getDisabledTextForeground();
485: textR.setSize(acceleratorWidth, fm.getHeight());
486: textR.x = viewR.x
487: + (isLTR ? viewR.width - textR.width - textIconGap
488: : textIconGap);
489: ButtonCommons.paintText(g, fm, acceleratorText, -1, textR,
490: acceleratorText, color);
491: }
492:
493: protected void paintBackground(final Graphics g,
494: final JMenuItem menuItem, final Color bgColor) {
495: if (menuItem.isOpaque() || isPaintArmed()) {
496: g.setColor(bgColor);
497: g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
498: }
499: }
500:
501: protected void paintText(final Graphics g,
502: final JMenuItem menuItem, final Rectangle textRect,
503: final String text) {
504: if (Utilities.isEmptyString(text)) {
505: return;
506: }
507:
508: FontMetrics fm = menuItem.getFontMetrics(menuItem.getFont());
509: String clippedText = Utilities.clipString(fm, text,
510: textRect.width);
511:
512: final Color color = menuItem.isEnabled() ? (isPaintArmed() ? selectionForeground
513: : menuItem.getForeground())
514: : getDisabledTextForeground();
515:
516: ButtonCommons.paintText(g, fm, clippedText, menuItem
517: .getDisplayedMnemonicIndex(), textRect, clippedText,
518: color);
519: }
520:
521: public MenuElement[] getPath() {
522: MenuElement[] selectedPath = MenuSelectionManager
523: .defaultManager().getSelectedPath();
524: if (Utilities.isEmptyArray(selectedPath)) {
525: return new MenuElement[0];
526: }
527:
528: MenuElement lastElement = selectedPath[selectedPath.length - 1];
529: boolean pathContainsItem = lastElement == menuItem;
530: if (!pathContainsItem && lastElement != menuItem.getParent()) {
531: return new MenuElement[] { menuItem };
532: }
533: int resultLength = selectedPath.length
534: + (pathContainsItem ? 0 : 1);
535: MenuElement[] result = new MenuElement[resultLength];
536: if (!pathContainsItem) {
537: result[resultLength - 1] = menuItem;
538: }
539: System.arraycopy(selectedPath, 0, result, 0,
540: selectedPath.length);
541:
542: return result;
543: }
544:
545: protected void doClick(final MenuSelectionManager msm) {
546: MenuSelectionManager manager = (msm != null) ? msm
547: : MenuSelectionManager.defaultManager();
548: manager.clearSelectedPath();
549: final BasicLookAndFeel lookAndFeel = (BasicLookAndFeel) UIManager
550: .getLookAndFeel();
551: lookAndFeel.fireSoundAction(menuItem, getPropertyPrefix()
552: + ".commandSound");
553: menuItem.doClick(0);
554: }
555:
556: boolean isArrowToBeDrawn() {
557: return arrowIcon != null;
558: }
559:
560: boolean isPaintArmed() {
561: return menuItem.isArmed();
562: }
563:
564: private Color getDisabledTextForeground() {
565: return (disabledForeground != null) ? disabledForeground
566: : menuItem.getBackground().darker();
567: }
568: }
|