1 /* BasicMenuItemUI.java --
2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.plaf.basic;
41 import gnu.classpath.SystemProperties;
43 import java.awt.Color;
44 import java.awt.Component;
45 import java.awt.Container;
46 import java.awt.Dimension;
48 import java.awt.FontMetrics;
49 import java.awt.Graphics;
50 import java.awt.Insets;
51 import java.awt.Rectangle;
52 import java.awt.event.ActionEvent;
53 import java.awt.event.ItemEvent;
54 import java.awt.event.ItemListener;
55 import java.awt.event.KeyEvent;
56 import java.awt.event.MouseEvent;
57 import java.awt.font.FontRenderContext;
58 import java.awt.font.TextLayout;
59 import java.awt.geom.AffineTransform;
60 import java.beans.PropertyChangeEvent;
61 import java.beans.PropertyChangeListener;
62 import java.util.ArrayList;
64 import javax.swing.AbstractAction;
65 import javax.swing.AbstractButton;
66 import javax.swing.ActionMap;
67 import javax.swing.ButtonModel;
68 import javax.swing.Icon;
69 import javax.swing.InputMap;
70 import javax.swing.JCheckBoxMenuItem;
71 import javax.swing.JComponent;
72 import javax.swing.JMenu;
73 import javax.swing.JMenuItem;
74 import javax.swing.JPopupMenu;
75 import javax.swing.KeyStroke;
76 import javax.swing.LookAndFeel;
77 import javax.swing.MenuElement;
78 import javax.swing.MenuSelectionManager;
79 import javax.swing.SwingConstants;
80 import javax.swing.SwingUtilities;
81 import javax.swing.UIDefaults;
82 import javax.swing.UIManager;
83 import javax.swing.event.MenuDragMouseEvent;
84 import javax.swing.event.MenuDragMouseListener;
85 import javax.swing.event.MenuKeyEvent;
86 import javax.swing.event.MenuKeyListener;
87 import javax.swing.event.MouseInputListener;
88 import javax.swing.plaf.ActionMapUIResource;
89 import javax.swing.plaf.ComponentInputMapUIResource;
90 import javax.swing.plaf.ComponentUI;
91 import javax.swing.plaf.MenuItemUI;
92 import javax.swing.text.View;
95 * UI Delegate for JMenuItem.
97 public class BasicMenuItemUI extends MenuItemUI
100 * Font to be used when displaying menu item's accelerator.
102 protected Font acceleratorFont;
105 * Color to be used when displaying menu item's accelerator.
107 protected Color acceleratorForeground;
110 * Color to be used when displaying menu item's accelerator when menu item is
113 protected Color acceleratorSelectionForeground;
116 * Icon that is displayed after the text to indicated that this menu contains
119 protected Icon arrowIcon;
122 * Icon that is displayed before the text. This icon is only used in
123 * JCheckBoxMenuItem or JRadioBoxMenuItem.
125 protected Icon checkIcon;
128 * Number of spaces between icon and text.
130 protected int defaultTextIconGap = 4;
133 * Color of the text when menu item is disabled
135 protected Color disabledForeground;
138 * The menu Drag mouse listener listening to the menu item.
140 protected MenuDragMouseListener menuDragMouseListener;
143 * The menu item itself
145 protected JMenuItem menuItem;
148 * Menu Key listener listening to the menu item.
150 protected MenuKeyListener menuKeyListener;
153 * mouse input listener listening to menu item.
155 protected MouseInputListener mouseInputListener;
158 * Indicates if border should be painted
160 protected boolean oldBorderPainted;
163 * Color of text that is used when menu item is selected
165 protected Color selectionBackground;
168 * Color of the text that is used when menu item is selected.
170 protected Color selectionForeground;
173 * String that separates description of the modifiers and the key
175 private String acceleratorDelimiter;
178 * ItemListener to listen for item changes in the menu item
180 private ItemListener itemListener;
183 * A PropertyChangeListener to make UI updates after property changes.
185 private PropertyChangeHandler propertyChangeListener;
188 * The view rectangle used for layout of the menu item.
190 private Rectangle viewRect;
193 * The rectangle that holds the area of the label.
195 private Rectangle textRect;
198 * The rectangle that holds the area of the accelerator.
200 private Rectangle accelRect;
203 * The rectangle that holds the area of the icon.
205 private Rectangle iconRect;
208 * The rectangle that holds the area of the icon.
210 private Rectangle arrowIconRect;
213 * The rectangle that holds the area of the check icon.
215 private Rectangle checkIconRect;
218 * A rectangle used for temporary storage to avoid creation of new
221 private Rectangle cachedRect;
224 * A class to handle PropertChangeEvents for the JMenuItem
225 * @author Anthony Balkissoon abalkiss at redhat dot com.
227 class PropertyChangeHandler implements PropertyChangeListener
230 * This method is called when a property of the menuItem is changed.
231 * Currently it is only used to update the accelerator key bindings.
234 * the PropertyChangeEvent
236 public void propertyChange(PropertyChangeEvent e)
238 String property = e.getPropertyName();
239 if (property.equals("accelerator"))
241 InputMap map = SwingUtilities.getUIInputMap(menuItem,
242 JComponent.WHEN_IN_FOCUSED_WINDOW);
244 map.remove((KeyStroke) e.getOldValue());
246 map = new ComponentInputMapUIResource(menuItem);
248 KeyStroke accelerator = (KeyStroke) e.getNewValue();
249 if (accelerator != null)
250 map.put(accelerator, "doClick");
252 // TextLayout caching for speed-up drawing of text.
253 else if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)
254 || property.equals("font"))
255 && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
258 AbstractButton b = (AbstractButton) e.getSource();
259 String text = b.getText();
262 FontRenderContext frc = new FontRenderContext(new AffineTransform(),
264 TextLayout layout = new TextLayout(text, b.getFont(), frc);
265 b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout);
271 * A class to handle accelerator keys. This is the Action we will
272 * perform when the accelerator key for this JMenuItem is pressed.
273 * @author Anthony Balkissoon abalkiss at redhat dot com
276 class ClickAction extends AbstractAction
279 * This is what is done when the accelerator key for the JMenuItem is
282 public void actionPerformed(ActionEvent event)
284 doClick(MenuSelectionManager.defaultManager());
289 * Creates a new BasicMenuItemUI object.
291 public BasicMenuItemUI()
293 mouseInputListener = createMouseInputListener(menuItem);
294 menuDragMouseListener = createMenuDragMouseListener(menuItem);
295 menuKeyListener = createMenuKeyListener(menuItem);
296 itemListener = new ItemHandler();
297 propertyChangeListener = new PropertyChangeHandler();
299 // Initialize rectangles for layout.
300 viewRect = new Rectangle();
301 textRect = new Rectangle();
302 iconRect = new Rectangle();
303 arrowIconRect = new Rectangle();
304 checkIconRect = new Rectangle();
305 accelRect = new Rectangle();
306 cachedRect = new Rectangle();
310 * Create MenuDragMouseListener to listen for mouse dragged events.
313 * menu item to listen to
314 * @return The MenuDragMouseListener
316 protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
318 return new MenuDragMouseHandler();
322 * Creates MenuKeyListener to listen to key events occuring when menu item is
323 * visible on the screen.
326 * menu item to listen to
327 * @return The MenuKeyListener
329 protected MenuKeyListener createMenuKeyListener(JComponent c)
331 return new MenuKeyHandler();
335 * Handles mouse input events occuring for this menu item
338 * menu item to listen to
339 * @return The MouseInputListener
341 protected MouseInputListener createMouseInputListener(JComponent c)
343 return new MouseInputHandler();
347 * Factory method to create a BasicMenuItemUI for the given {@link
348 * JComponent}, which should be a {@link JMenuItem}.
351 * The {@link JComponent} a UI is being created for.
352 * @return A BasicMenuItemUI for the {@link JComponent}.
354 public static ComponentUI createUI(JComponent c)
356 return new BasicMenuItemUI();
360 * Programatically clicks menu item.
363 * MenuSelectionManager for the menu hierarchy
365 protected void doClick(MenuSelectionManager msm)
368 msm.clearSelectedPath();
372 * Returns maximum size for the specified menu item
375 * component for which to get maximum size
376 * @return Maximum size for the specified menu item.
378 public Dimension getMaximumSize(JComponent c)
384 * Returns minimum size for the specified menu item
387 * component for which to get minimum size
388 * @return Minimum size for the specified menu item.
390 public Dimension getMinimumSize(JComponent c)
396 * Returns path to this menu item.
398 * @return $MenuElement[]$ Returns array of menu elements that constitute a
399 * path to this menu item.
401 public MenuElement[] getPath()
403 ArrayList path = new ArrayList();
405 Component c = menuItem;
406 while (c instanceof MenuElement)
410 if (c instanceof JPopupMenu)
411 c = ((JPopupMenu) c).getInvoker();
416 MenuElement[] pathArray = new MenuElement[path.size()];
417 path.toArray(pathArray);
422 * Returns preferred size for the given menu item.
425 * menu item for which to get preferred size
427 * check icon displayed in the given menu item
429 * arrow icon displayed in the given menu item
430 * @param defaultTextIconGap
431 * space between icon and text in the given menuItem
432 * @return $Dimension$ preferred size for the given menu item
434 protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon,
436 int defaultTextIconGap)
438 JMenuItem m = (JMenuItem) c;
439 String accelText = getAcceleratorString(m);
441 // Layout the menu item. The result gets stored in the rectangle
442 // fields of this class.
443 resetRectangles(null);
444 layoutMenuItem(m, accelText);
446 // The union of the text and icon areas is the label area.
447 cachedRect.setBounds(textRect);
448 Rectangle pref = SwingUtilities.computeUnion(iconRect.x, iconRect.y,
453 // Find the widest menu item text and accelerator and store it in
454 // client properties of the parent, so that we can align the accelerators
455 // properly. Of course, we only need can do this, if the parent is
456 // a JComponent and this menu item is not a toplevel menu.
457 Container parent = m.getParent();
458 if (parent != null && parent instanceof JComponent
459 && !(m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
461 JComponent p = (JComponent) parent;
463 // The widest text so far.
464 Integer maxTextWidth = (Integer) p.getClientProperty("maxTextWidth");
465 int maxTextValue = maxTextWidth == null ? 0 : maxTextWidth.intValue();
466 if (pref.width < maxTextValue)
467 pref.width = maxTextValue;
469 p.putClientProperty("maxTextWidth", new Integer(pref.width));
471 // The widest accelerator so far.
472 Integer maxAccelWidth = (Integer) p.getClientProperty("maxAccelWidth");
473 int maxAccelValue = maxAccelWidth == null ? 0
474 : maxAccelWidth.intValue();
475 if (accelRect.width > maxAccelValue)
477 maxAccelValue = accelRect.width;
478 p.putClientProperty("maxAccelWidth", new Integer(accelRect.width));
480 pref.width += maxAccelValue;
481 pref.width += defaultTextIconGap;
484 // Add arrow and check size if appropriate.
485 if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
487 pref.width += checkIconRect.width;
488 pref.width += defaultTextIconGap;
489 pref.width += arrowIconRect.width;
490 pref.width += defaultTextIconGap;
493 // Add a gap ~2 times as wide as the defaultTextIconGap.
494 pref.width += 2 * defaultTextIconGap;
496 // Respect the insets of the menu item.
497 Insets i = m.getInsets();
498 pref.width += i.left + i.right;
499 pref.height += i.top + i.bottom;
501 // Return a copy, so that nobody messes with our textRect.
502 return pref.getSize();
506 * Returns preferred size of the given component
509 * component for which to return preferred size
510 * @return $Dimension$ preferred size for the given component
512 public Dimension getPreferredSize(JComponent c)
514 return getPreferredMenuItemSize(c, checkIcon, arrowIcon,
519 * Returns the prefix for entries in the {@link UIDefaults} table.
523 protected String getPropertyPrefix()
529 * This method installs the components for this {@link JMenuItem}.
532 * The {@link JMenuItem} to install components for.
534 protected void installComponents(JMenuItem menuItem)
536 // FIXME: Need to implement
540 * This method installs the defaults that are defined in the Basic look and
541 * feel for this {@link JMenuItem}.
543 protected void installDefaults()
545 String prefix = getPropertyPrefix();
546 LookAndFeel.installBorder(menuItem, prefix + ".border");
547 LookAndFeel.installColorsAndFont(menuItem, prefix + ".background",
548 prefix + ".foreground", prefix + ".font");
549 menuItem.setMargin(UIManager.getInsets(prefix + ".margin"));
550 acceleratorFont = UIManager.getFont(prefix + ".acceleratorFont");
551 acceleratorForeground = UIManager.getColor(prefix
552 + ".acceleratorForeground");
553 acceleratorSelectionForeground = UIManager.getColor(prefix
554 + ".acceleratorSelectionForeground");
555 selectionBackground = UIManager.getColor(prefix + ".selectionBackground");
556 selectionForeground = UIManager.getColor(prefix + ".selectionForeground");
557 acceleratorDelimiter = UIManager.getString(prefix + ".acceleratorDelimiter");
558 checkIcon = UIManager.getIcon(prefix + ".checkIcon");
560 menuItem.setHorizontalTextPosition(SwingConstants.TRAILING);
561 menuItem.setHorizontalAlignment(SwingConstants.LEADING);
565 * This method installs the keyboard actions for this {@link JMenuItem}.
567 protected void installKeyboardActions()
569 InputMap focusedWindowMap = SwingUtilities.getUIInputMap(menuItem,
570 JComponent.WHEN_IN_FOCUSED_WINDOW);
571 if (focusedWindowMap == null)
572 focusedWindowMap = new ComponentInputMapUIResource(menuItem);
573 KeyStroke accelerator = menuItem.getAccelerator();
574 if (accelerator != null)
575 focusedWindowMap.put(accelerator, "doClick");
576 SwingUtilities.replaceUIInputMap(menuItem,
577 JComponent.WHEN_IN_FOCUSED_WINDOW, focusedWindowMap);
579 ActionMap UIActionMap = SwingUtilities.getUIActionMap(menuItem);
580 if (UIActionMap == null)
581 UIActionMap = new ActionMapUIResource();
582 UIActionMap.put("doClick", new ClickAction());
583 SwingUtilities.replaceUIActionMap(menuItem, UIActionMap);
587 * This method installs the listeners for the {@link JMenuItem}.
589 protected void installListeners()
591 menuItem.addMouseListener(mouseInputListener);
592 menuItem.addMouseMotionListener(mouseInputListener);
593 menuItem.addMenuDragMouseListener(menuDragMouseListener);
594 menuItem.addMenuKeyListener(menuKeyListener);
595 menuItem.addItemListener(itemListener);
596 menuItem.addPropertyChangeListener(propertyChangeListener);
597 // Fire synthetic property change event to let the listener update
598 // the TextLayout cache.
599 propertyChangeListener.propertyChange(new PropertyChangeEvent(menuItem,
601 menuItem.getFont()));
605 * Installs and initializes all fields for this UI delegate. Any properties of
606 * the UI that need to be initialized and/or set to defaults will be done now.
607 * It will also install any listeners necessary.
610 * The {@link JComponent} that is having this UI installed.
612 public void installUI(JComponent c)
615 menuItem = (JMenuItem) c;
617 installComponents(menuItem);
619 installKeyboardActions();
623 * Paints given menu item using specified graphics context
626 * The graphics context used to paint this menu item
630 public void paint(Graphics g, JComponent c)
632 paintMenuItem(g, c, checkIcon, arrowIcon, selectionBackground,
633 c.getForeground(), defaultTextIconGap);
637 * Paints background of the menu item
640 * The graphics context used to paint this menu item
644 * Background color to use when painting menu item
646 protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor)
648 // Menu item is considered to be highlighted when it is selected.
649 // But we don't want to paint the background of JCheckBoxMenuItems
650 ButtonModel mod = menuItem.getModel();
651 Color saved = g.getColor();
652 if (mod.isArmed() || ((menuItem instanceof JMenu) && mod.isSelected()))
655 g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
657 else if (menuItem.isOpaque())
659 g.setColor(menuItem.getBackground());
660 g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
666 * Paints specified menu item
669 * The graphics context used to paint this menu item
673 * check icon to use when painting menu item
675 * arrow icon to use when painting menu item
677 * Background color of the menu item
679 * Foreground color of the menu item
680 * @param defaultTextIconGap
681 * space to use between icon and text when painting menu item
683 protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
684 Icon arrowIcon, Color background,
685 Color foreground, int defaultTextIconGap)
687 JMenuItem m = (JMenuItem) c;
690 Font oldFont = g.getFont();
691 Font font = c.getFont();
693 FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
695 // Create accelerator string.
696 String accelText = getAcceleratorString(m);
698 // Layout menu item. The result gets stored in the rectangle fields
702 layoutMenuItem(m, accelText);
704 // Paint the background.
705 paintBackground(g, m, background);
707 Color oldColor = g.getColor();
709 // Paint the check icon.
710 if (checkIcon != null)
712 checkIcon.paintIcon(m, g, checkIconRect.x, checkIconRect.y);
716 ButtonModel model = m.getModel();
717 if (m.getIcon() != null)
719 // Determine icon depending on the menu item
720 // state (normal/disabled/pressed).
724 icon = m.getDisabledIcon();
726 else if (model.isPressed() && model.isArmed())
728 icon = m.getPressedIcon();
741 icon.paintIcon(m, g, iconRect.x, iconRect.y);
746 String text = m.getText();
750 View html = (View) m.getClientProperty(BasicHTML.propertyKey);
753 html.paint(g, textRect);
757 paintText(g, m, textRect, text);
761 // Paint accelerator text.
762 if (! accelText.equals(""))
764 // Align the accelerator text. In getPreferredMenuItemSize() we
765 // store a client property 'maxAccelWidth' in the parent which holds
766 // the maximum accelerator width for the children of this parent.
767 // We use this here to align the accelerators properly.
769 Container parent = m.getParent();
770 if (parent != null && parent instanceof JComponent)
772 JComponent p = (JComponent) parent;
773 Integer maxAccelWidth =
774 (Integer) p.getClientProperty("maxAccelWidth");
775 int maxAccelValue = maxAccelWidth == null ? 0
776 : maxAccelWidth.intValue();
777 accelOffset = maxAccelValue - accelRect.width;
780 g.setFont(acceleratorFont);
783 // Paint accelerator disabled.
784 g.setColor(disabledForeground);
788 if (m.isArmed() || (m instanceof JMenu && m.isSelected()))
789 g.setColor(acceleratorSelectionForeground);
791 g.setColor(acceleratorForeground);
793 g.drawString(accelText, accelRect.x - accelOffset,
794 accelRect.y + accelFm.getAscent());
798 if (arrowIcon != null
799 && ! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
801 arrowIcon.paintIcon(m, g, arrowIconRect.x, arrowIconRect.y);
805 g.setColor(oldColor);
810 * Paints label for the given menu item
813 * The graphics context used to paint this menu item
815 * menu item for which to draw its label
817 * rectangle specifiying position of the text relative to the given
820 * label of the menu item
822 protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect,
825 Font f = menuItem.getFont();
827 FontMetrics fm = g.getFontMetrics(f);
829 if (text != null && !text.equals(""))
831 if (menuItem.isEnabled())
833 // Menu item is considered to be highlighted when it is selected.
834 // But not if it's a JCheckBoxMenuItem
835 ButtonModel mod = menuItem.getModel();
836 if ((menuItem.isSelected() && checkIcon == null)
837 || (mod != null && mod.isArmed())
838 && (menuItem.getParent() instanceof MenuElement))
839 g.setColor(selectionForeground);
841 g.setColor(menuItem.getForeground());
844 // FIXME: should fix this to use 'disabledForeground', but its
845 // default value in BasicLookAndFeel is null.
847 // FIXME: should there be different foreground colours for selected
848 // or deselected, when disabled?
849 g.setColor(Color.gray);
851 int mnemonicIndex = menuItem.getDisplayedMnemonicIndex();
853 if (mnemonicIndex != -1)
854 BasicGraphicsUtils.drawStringUnderlineCharAt(menuItem, g, text,
860 BasicGraphicsUtils.drawString(menuItem, g, text, 0, textRect.x,
861 textRect.y + fm.getAscent());
866 * This method uninstalls the components for this {@link JMenuItem}.
869 * The {@link JMenuItem} to uninstall components for.
871 protected void uninstallComponents(JMenuItem menuItem)
873 // FIXME: need to implement
877 * This method uninstalls the defaults and sets any objects created during
880 protected void uninstallDefaults()
882 menuItem.setForeground(null);
883 menuItem.setBackground(null);
884 menuItem.setBorder(null);
885 menuItem.setMargin(null);
886 menuItem.setBackground(null);
887 menuItem.setBorder(null);
888 menuItem.setFont(null);
889 menuItem.setForeground(null);
890 menuItem.setMargin(null);
891 acceleratorFont = null;
892 acceleratorForeground = null;
893 acceleratorSelectionForeground = null;
895 selectionBackground = null;
896 selectionForeground = null;
897 acceleratorDelimiter = null;
901 * Uninstalls any keyboard actions.
903 protected void uninstallKeyboardActions()
905 SwingUtilities.replaceUIInputMap(menuItem,
906 JComponent.WHEN_IN_FOCUSED_WINDOW, null);
910 * Unregisters all the listeners that this UI delegate was using.
912 protected void uninstallListeners()
914 menuItem.removeMouseListener(mouseInputListener);
915 menuItem.removeMenuDragMouseListener(menuDragMouseListener);
916 menuItem.removeMenuKeyListener(menuKeyListener);
917 menuItem.removeItemListener(itemListener);
918 menuItem.removePropertyChangeListener(propertyChangeListener);
922 * Performs the opposite of installUI. Any properties or resources that need
923 * to be cleaned up will be done now. It will also uninstall any listeners it
924 * has. In addition, any properties of this UI will be nulled.
927 * The {@link JComponent} that is having this UI uninstalled.
929 public void uninstallUI(JComponent c)
931 uninstallListeners();
933 uninstallComponents(menuItem);
934 c.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
939 * This method calls paint.
942 * The graphics context used to paint this menu item
944 * The menu item to paint
946 public void update(Graphics g, JComponent c)
952 * This class handles mouse events occuring inside the menu item. Most of the
953 * events are forwarded for processing to MenuSelectionManager of the current
956 protected class MouseInputHandler implements MouseInputListener
959 * Creates a new MouseInputHandler object.
961 protected MouseInputHandler()
963 // Nothing to do here.
967 * This method is called when mouse is clicked on the menu item. It forwards
968 * this event to MenuSelectionManager.
971 * A {@link MouseEvent}.
973 public void mouseClicked(MouseEvent e)
975 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
976 manager.processMouseEvent(e);
980 * This method is called when mouse is dragged inside the menu item. It
981 * forwards this event to MenuSelectionManager.
984 * A {@link MouseEvent}.
986 public void mouseDragged(MouseEvent e)
988 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
989 manager.processMouseEvent(e);
993 * This method is called when mouse enters menu item. When this happens menu
994 * item is considered to be selected and selection path in
995 * MenuSelectionManager is set. This event is also forwarded to
996 * MenuSelection Manager for further processing.
999 * A {@link MouseEvent}.
1001 public void mouseEntered(MouseEvent e)
1003 Component source = (Component) e.getSource();
1004 if (source.getParent() instanceof MenuElement)
1006 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1007 manager.setSelectedPath(getPath());
1008 manager.processMouseEvent(e);
1013 * This method is called when mouse exits menu item. The event is forwarded
1014 * to MenuSelectionManager for processing.
1017 * A {@link MouseEvent}.
1019 public void mouseExited(MouseEvent e)
1021 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1022 manager.processMouseEvent(e);
1026 * This method is called when mouse is inside the menu item. This event is
1027 * forwarder to MenuSelectionManager for further processing.
1030 * A {@link MouseEvent}.
1032 public void mouseMoved(MouseEvent e)
1034 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1035 manager.processMouseEvent(e);
1039 * This method is called when mouse is pressed. This event is forwarded to
1040 * MenuSelectionManager for further processing.
1043 * A {@link MouseEvent}.
1045 public void mousePressed(MouseEvent e)
1047 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1048 manager.processMouseEvent(e);
1052 * This method is called when mouse is released. If the mouse is released
1053 * inside this menuItem, then this menu item is considered to be chosen and
1054 * the menu hierarchy should be closed.
1057 * A {@link MouseEvent}.
1059 public void mouseReleased(MouseEvent e)
1061 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1064 if (x > 0 && x < menuItem.getWidth() && y > 0
1065 && y < menuItem.getHeight())
1070 manager.processMouseEvent(e);
1075 * This class handles mouse dragged events.
1077 private class MenuDragMouseHandler implements MenuDragMouseListener
1080 * Tbis method is invoked when mouse is dragged over the menu item.
1083 * The MenuDragMouseEvent
1085 public void menuDragMouseDragged(MenuDragMouseEvent e)
1087 MenuSelectionManager manager = e.getMenuSelectionManager();
1088 manager.setSelectedPath(e.getPath());
1092 * Tbis method is invoked when mouse enters the menu item while it is being
1096 * The MenuDragMouseEvent
1098 public void menuDragMouseEntered(MenuDragMouseEvent e)
1100 MenuSelectionManager manager = e.getMenuSelectionManager();
1101 manager.setSelectedPath(e.getPath());
1105 * Tbis method is invoked when mouse exits the menu item while it is being
1108 * @param e the MenuDragMouseEvent
1110 public void menuDragMouseExited(MenuDragMouseEvent e)
1112 // Nothing to do here yet.
1116 * Tbis method is invoked when mouse was dragged and released inside the
1120 * The MenuDragMouseEvent
1122 public void menuDragMouseReleased(MenuDragMouseEvent e)
1124 MenuSelectionManager manager = e.getMenuSelectionManager();
1127 if (x >= 0 && x < menuItem.getWidth() && y >= 0
1128 && y < menuItem.getHeight())
1131 manager.clearSelectedPath();
1136 * This class handles key events occuring when menu item is visible on the
1139 private class MenuKeyHandler implements MenuKeyListener
1142 * This method is invoked when key has been pressed
1145 * A {@link MenuKeyEvent}.
1147 public void menuKeyPressed(MenuKeyEvent e)
1149 // TODO: What should be done here, if anything?
1153 * This method is invoked when key has been pressed
1156 * A {@link MenuKeyEvent}.
1158 public void menuKeyReleased(MenuKeyEvent e)
1160 // TODO: What should be done here, if anything?
1164 * This method is invoked when key has been typed It handles the mnemonic
1165 * key for the menu item.
1168 * A {@link MenuKeyEvent}.
1170 public void menuKeyTyped(MenuKeyEvent e)
1172 // TODO: What should be done here, if anything?
1177 * Helper class that listens for item changes to the properties of the {@link
1180 private class ItemHandler implements ItemListener
1183 * This method is called when one of the menu item changes.
1185 * @param evt A {@link ItemEvent}.
1187 public void itemStateChanged(ItemEvent evt)
1189 boolean state = false;
1190 if (menuItem instanceof JCheckBoxMenuItem)
1192 if (evt.getStateChange() == ItemEvent.SELECTED)
1194 ((JCheckBoxMenuItem) menuItem).setState(state);
1196 menuItem.revalidate();
1202 * A helper method to create the accelerator string from the menu item's
1203 * accelerator property. The returned string is empty if there is
1204 * no accelerator defined.
1206 * @param m the menu item
1208 * @return the accelerator string, not null
1210 private String getAcceleratorString(JMenuItem m)
1212 // Create accelerator string.
1213 KeyStroke accel = m.getAccelerator();
1214 String accelText = "";
1217 int mods = accel.getModifiers();
1220 accelText = KeyEvent.getKeyModifiersText(mods);
1221 accelText += acceleratorDelimiter;
1223 int keycode = accel.getKeyCode();
1225 accelText += KeyEvent.getKeyText(keycode);
1227 accelText += accel.getKeyChar();
1233 * Resets the cached layout rectangles. If <code>i</code> is not null, then
1234 * the view rectangle is set to the inner area of the component, otherwise
1235 * it is set to (0, 0, Short.MAX_VALUE, Short.MAX_VALUE), this is needed
1238 * @param i the component for which to initialize the rectangles
1240 private void resetRectangles(JMenuItem i)
1242 // Reset rectangles.
1243 iconRect.setBounds(0, 0, 0, 0);
1244 textRect.setBounds(0, 0, 0, 0);
1245 accelRect.setBounds(0, 0, 0, 0);
1246 checkIconRect.setBounds(0, 0, 0, 0);
1247 arrowIconRect.setBounds(0, 0, 0, 0);
1249 viewRect.setBounds(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
1252 Insets insets = i.getInsets();
1253 viewRect.setBounds(insets.left, insets.top,
1254 i.getWidth() - insets.left - insets.right,
1255 i.getHeight() - insets.top - insets.bottom);
1260 * A helper method that lays out the menu item. The layout is stored
1261 * in the fields of this class.
1263 * @param m the menu item to layout
1264 * @param accelText the accelerator text
1266 private void layoutMenuItem(JMenuItem m, String accelText)
1269 Font font = m.getFont();
1270 FontMetrics fm = m.getFontMetrics(font);
1271 FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
1273 String text = m.getText();
1274 SwingUtilities.layoutCompoundLabel(m, fm, text, m.getIcon(),
1275 m.getVerticalAlignment(),
1276 m.getHorizontalAlignment(),
1277 m.getVerticalTextPosition(),
1278 m.getHorizontalTextPosition(),
1279 viewRect, iconRect, textRect,
1280 defaultTextIconGap);
1282 // Initialize accelerator width and height.
1283 if (! accelText.equals(""))
1285 accelRect.width = accelFm.stringWidth(accelText);
1286 accelRect.height = accelFm.getHeight();
1289 // Initialize check and arrow icon width and height.
1290 if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
1292 if (checkIcon != null)
1294 checkIconRect.width = checkIcon.getIconWidth();
1295 checkIconRect.height = checkIcon.getIconHeight();
1297 if (arrowIcon != null)
1299 arrowIconRect.width = arrowIcon.getIconWidth();
1300 arrowIconRect.height = arrowIcon.getIconHeight();
1304 // The union of the icon and text of the menu item is the 'label area'.
1305 cachedRect.setBounds(textRect);
1306 Rectangle labelRect = SwingUtilities.computeUnion(iconRect.x,
1311 textRect.x += defaultTextIconGap;
1312 iconRect.x += defaultTextIconGap;
1314 // Layout accelerator rect.
1315 accelRect.x = viewRect.x + viewRect.width - arrowIconRect.width
1316 - defaultTextIconGap - accelRect.width;
1317 // Layout check and arrow icons only when not in toplevel menu.
1318 if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
1320 checkIconRect.x = viewRect.x + defaultTextIconGap;
1321 textRect.x += defaultTextIconGap + checkIconRect.width;
1322 iconRect.x += defaultTextIconGap + checkIconRect.width;
1323 arrowIconRect.x = viewRect.x + viewRect.width - defaultTextIconGap
1324 - arrowIconRect.width;
1327 // Align the accelerator text and all the icons vertically centered to
1329 accelRect.y = labelRect.y + (labelRect.height / 2)
1330 - (accelRect.height / 2);
1331 if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
1333 arrowIconRect.y = labelRect.y + (labelRect.height / 2)
1334 - (arrowIconRect.height / 2);
1335 checkIconRect.y = labelRect.y + (labelRect.height / 2)
1336 - (checkIconRect.height / 2);