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 java.awt.Color;
42 import java.awt.Component;
43 import java.awt.Container;
44 import java.awt.Dimension;
46 import java.awt.FontMetrics;
47 import java.awt.Graphics;
48 import java.awt.Insets;
49 import java.awt.Rectangle;
50 import java.awt.event.ActionEvent;
51 import java.awt.event.ItemEvent;
52 import java.awt.event.ItemListener;
53 import java.awt.event.KeyEvent;
54 import java.awt.event.MouseEvent;
55 import java.beans.PropertyChangeEvent;
56 import java.beans.PropertyChangeListener;
57 import java.util.ArrayList;
59 import javax.swing.AbstractAction;
60 import javax.swing.ActionMap;
61 import javax.swing.ButtonModel;
62 import javax.swing.Icon;
63 import javax.swing.InputMap;
64 import javax.swing.JCheckBoxMenuItem;
65 import javax.swing.JComponent;
66 import javax.swing.JMenu;
67 import javax.swing.JMenuItem;
68 import javax.swing.JPopupMenu;
69 import javax.swing.KeyStroke;
70 import javax.swing.LookAndFeel;
71 import javax.swing.MenuElement;
72 import javax.swing.MenuSelectionManager;
73 import javax.swing.SwingConstants;
74 import javax.swing.SwingUtilities;
75 import javax.swing.UIDefaults;
76 import javax.swing.UIManager;
77 import javax.swing.event.MenuDragMouseEvent;
78 import javax.swing.event.MenuDragMouseListener;
79 import javax.swing.event.MenuKeyEvent;
80 import javax.swing.event.MenuKeyListener;
81 import javax.swing.event.MouseInputListener;
82 import javax.swing.plaf.ActionMapUIResource;
83 import javax.swing.plaf.ComponentInputMapUIResource;
84 import javax.swing.plaf.ComponentUI;
85 import javax.swing.plaf.MenuItemUI;
86 import javax.swing.text.View;
89 * UI Delegate for JMenuItem.
91 public class BasicMenuItemUI extends MenuItemUI
94 * Font to be used when displaying menu item's accelerator.
96 protected Font acceleratorFont;
99 * Color to be used when displaying menu item's accelerator.
101 protected Color acceleratorForeground;
104 * Color to be used when displaying menu item's accelerator when menu item is
107 protected Color acceleratorSelectionForeground;
110 * Icon that is displayed after the text to indicated that this menu contains
113 protected Icon arrowIcon;
116 * Icon that is displayed before the text. This icon is only used in
117 * JCheckBoxMenuItem or JRadioBoxMenuItem.
119 protected Icon checkIcon;
122 * Number of spaces between icon and text.
124 protected int defaultTextIconGap = 4;
127 * Color of the text when menu item is disabled
129 protected Color disabledForeground;
132 * The menu Drag mouse listener listening to the menu item.
134 protected MenuDragMouseListener menuDragMouseListener;
137 * The menu item itself
139 protected JMenuItem menuItem;
142 * Menu Key listener listening to the menu item.
144 protected MenuKeyListener menuKeyListener;
147 * mouse input listener listening to menu item.
149 protected MouseInputListener mouseInputListener;
152 * Indicates if border should be painted
154 protected boolean oldBorderPainted;
157 * Color of text that is used when menu item is selected
159 protected Color selectionBackground;
162 * Color of the text that is used when menu item is selected.
164 protected Color selectionForeground;
167 * String that separates description of the modifiers and the key
169 private String acceleratorDelimiter;
172 * ItemListener to listen for item changes in the menu item
174 private ItemListener itemListener;
177 * Number of spaces between accelerator and menu item's label.
179 private int defaultAcceleratorLabelGap = 10;
182 * The gap between different menus on the MenuBar.
184 private int MenuGap = 10;
186 /** A PropertyChangeListener to make UI updates after property changes **/
187 PropertyChangeHandler propertyChangeListener;
190 * The view rectangle used for layout of the menu item.
192 private Rectangle viewRect;
195 * The rectangle that holds the area of the label.
197 private Rectangle textRect;
200 * The rectangle that holds the area of the accelerator.
202 private Rectangle accelRect;
205 * The rectangle that holds the area of the icon.
207 private Rectangle iconRect;
210 * The rectangle that holds the area of the icon.
212 private Rectangle arrowIconRect;
215 * The rectangle that holds the area of the check icon.
217 private Rectangle checkIconRect;
220 * A rectangle used for temporary storage to avoid creation of new
223 private Rectangle cachedRect;
226 * A class to handle PropertChangeEvents for the JMenuItem
227 * @author Anthony Balkissoon abalkiss at redhat dot com.
229 class PropertyChangeHandler implements PropertyChangeListener
232 * This method is called when a property of the menuItem is changed.
233 * Currently it is only used to update the accelerator key bindings.
236 * the PropertyChangeEvent
238 public void propertyChange(PropertyChangeEvent e)
240 if (e.getPropertyName() == "accelerator")
242 InputMap map = SwingUtilities.getUIInputMap(menuItem,
243 JComponent.WHEN_IN_FOCUSED_WINDOW);
245 map.remove((KeyStroke) e.getOldValue());
247 map = new ComponentInputMapUIResource(menuItem);
249 KeyStroke accelerator = (KeyStroke) e.getNewValue();
250 if (accelerator != null)
251 map.put(accelerator, "doClick");
257 * A class to handle accelerator keys. This is the Action we will
258 * perform when the accelerator key for this JMenuItem is pressed.
259 * @author Anthony Balkissoon abalkiss at redhat dot com
262 class ClickAction extends AbstractAction
265 * This is what is done when the accelerator key for the JMenuItem is
268 public void actionPerformed(ActionEvent event)
270 doClick(MenuSelectionManager.defaultManager());
275 * Creates a new BasicMenuItemUI object.
277 public BasicMenuItemUI()
279 mouseInputListener = createMouseInputListener(menuItem);
280 menuDragMouseListener = createMenuDragMouseListener(menuItem);
281 menuKeyListener = createMenuKeyListener(menuItem);
282 itemListener = new ItemHandler();
283 propertyChangeListener = new PropertyChangeHandler();
285 // Initialize rectangles for layout.
286 viewRect = new Rectangle();
287 textRect = new Rectangle();
288 iconRect = new Rectangle();
289 arrowIconRect = new Rectangle();
290 checkIconRect = new Rectangle();
291 accelRect = new Rectangle();
292 cachedRect = new Rectangle();
296 * Create MenuDragMouseListener to listen for mouse dragged events.
299 * menu item to listen to
300 * @return The MenuDragMouseListener
302 protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
304 return new MenuDragMouseHandler();
308 * Creates MenuKeyListener to listen to key events occuring when menu item is
309 * visible on the screen.
312 * menu item to listen to
313 * @return The MenuKeyListener
315 protected MenuKeyListener createMenuKeyListener(JComponent c)
317 return new MenuKeyHandler();
321 * Handles mouse input events occuring for this menu item
324 * menu item to listen to
325 * @return The MouseInputListener
327 protected MouseInputListener createMouseInputListener(JComponent c)
329 return new MouseInputHandler();
333 * Factory method to create a BasicMenuItemUI for the given {@link
334 * JComponent}, which should be a {@link JMenuItem}.
337 * The {@link JComponent} a UI is being created for.
338 * @return A BasicMenuItemUI for the {@link JComponent}.
340 public static ComponentUI createUI(JComponent c)
342 return new BasicMenuItemUI();
346 * Programatically clicks menu item.
349 * MenuSelectionManager for the menu hierarchy
351 protected void doClick(MenuSelectionManager msm)
354 msm.clearSelectedPath();
358 * Returns maximum size for the specified menu item
361 * component for which to get maximum size
362 * @return Maximum size for the specified menu item.
364 public Dimension getMaximumSize(JComponent c)
370 * Returns minimum size for the specified menu item
373 * component for which to get minimum size
374 * @return Minimum size for the specified menu item.
376 public Dimension getMinimumSize(JComponent c)
382 * Returns path to this menu item.
384 * @return $MenuElement[]$ Returns array of menu elements that constitute a
385 * path to this menu item.
387 public MenuElement[] getPath()
389 ArrayList path = new ArrayList();
391 // Path to menu should also include its popup menu.
392 if (menuItem instanceof JMenu)
393 path.add(((JMenu) menuItem).getPopupMenu());
395 Component c = menuItem;
396 while (c instanceof MenuElement)
398 path.add(0, (MenuElement) c);
400 if (c instanceof JPopupMenu)
401 c = ((JPopupMenu) c).getInvoker();
406 MenuElement[] pathArray = new MenuElement[path.size()];
407 path.toArray(pathArray);
412 * Returns preferred size for the given menu item.
415 * menu item for which to get preferred size
417 * check icon displayed in the given menu item
419 * arrow icon displayed in the given menu item
420 * @param defaultTextIconGap
421 * space between icon and text in the given menuItem
422 * @return $Dimension$ preferred size for the given menu item
424 protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon,
426 int defaultTextIconGap)
428 JMenuItem m = (JMenuItem) c;
429 String accelText = getAcceleratorString(m);
431 // Layout the menu item. The result gets stored in the rectangle
432 // fields of this class.
433 layoutMenuItem(m, accelText);
435 // The union of the text and icon areas is the label area.
436 cachedRect.setBounds(textRect);
437 Rectangle pref = SwingUtilities.computeUnion(iconRect.x, iconRect.y,
442 // Find the widest menu item text and accelerator and store it in
443 // client properties of the parent, so that we can align the accelerators
444 // properly. Of course, we only need can do this, if the parent is
445 // a JComponent and this menu item is not a toplevel menu.
446 Container parent = m.getParent();
447 if (parent != null && parent instanceof JComponent
448 && !(m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
450 JComponent p = (JComponent) parent;
452 // The widest text so far.
453 Integer maxTextWidth = (Integer) p.getClientProperty("maxTextWidth");
454 int maxTextValue = maxTextWidth == null ? 0 : maxTextWidth.intValue();
455 if (pref.width < maxTextValue)
456 pref.width = maxTextValue;
458 p.putClientProperty("maxTextWidth", new Integer(pref.width));
460 // The widest accelerator so far.
461 Integer maxAccelWidth = (Integer) p.getClientProperty("maxAccelWidth");
462 int maxAccelValue = maxAccelWidth == null ? 0
463 : maxAccelWidth.intValue();
464 if (accelRect.width > maxAccelValue)
466 maxAccelValue = accelRect.width;
467 p.putClientProperty("maxAccelWidth", new Integer(accelRect.width));
469 pref.width += maxAccelValue;
470 pref.width += defaultTextIconGap;
473 // Add arrow and check size if appropriate.
474 if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
476 pref.width += checkIconRect.width;
477 pref.width += defaultTextIconGap;
478 pref.width += arrowIconRect.width;
479 pref.width += defaultTextIconGap;
482 // Add a gap ~2 times as wide as the defaultTextIconGap.
483 pref.width += 2 * defaultTextIconGap;
485 // Respect the insets of the menu item.
486 Insets i = m.getInsets();
487 pref.width += i.left + i.right;
488 pref.height += i.top + i.bottom;
490 // Return a copy, so that nobody messes with our textRect.
491 return pref.getSize();
495 * Returns preferred size of the given component
498 * component for which to return preferred size
499 * @return $Dimension$ preferred size for the given component
501 public Dimension getPreferredSize(JComponent c)
503 return getPreferredMenuItemSize(c, checkIcon, arrowIcon,
508 * Returns the prefix for entries in the {@link UIDefaults} table.
512 protected String getPropertyPrefix()
518 * This method installs the components for this {@link JMenuItem}.
521 * The {@link JMenuItem} to install components for.
523 protected void installComponents(JMenuItem menuItem)
525 // FIXME: Need to implement
529 * This method installs the defaults that are defined in the Basic look and
530 * feel for this {@link JMenuItem}.
532 protected void installDefaults()
534 String prefix = getPropertyPrefix();
535 LookAndFeel.installBorder(menuItem, prefix + ".border");
536 LookAndFeel.installColorsAndFont(menuItem, prefix + ".background",
537 prefix + ".foreground", prefix + ".font");
538 menuItem.setMargin(UIManager.getInsets(prefix + ".margin"));
539 acceleratorFont = UIManager.getFont(prefix + ".acceleratorFont");
540 acceleratorForeground = UIManager.getColor(prefix
541 + ".acceleratorForeground");
542 acceleratorSelectionForeground = UIManager.getColor(prefix
543 + ".acceleratorSelectionForeground");
544 selectionBackground = UIManager.getColor(prefix + ".selectionBackground");
545 selectionForeground = UIManager.getColor(prefix + ".selectionForeground");
546 acceleratorDelimiter = UIManager.getString(prefix + ".acceleratorDelimiter");
547 checkIcon = UIManager.getIcon(prefix + ".checkIcon");
549 menuItem.setHorizontalTextPosition(SwingConstants.TRAILING);
550 menuItem.setHorizontalAlignment(SwingConstants.LEADING);
554 * This method installs the keyboard actions for this {@link JMenuItem}.
556 protected void installKeyboardActions()
558 InputMap focusedWindowMap = SwingUtilities.getUIInputMap(menuItem,
559 JComponent.WHEN_IN_FOCUSED_WINDOW);
560 if (focusedWindowMap == null)
561 focusedWindowMap = new ComponentInputMapUIResource(menuItem);
562 KeyStroke accelerator = menuItem.getAccelerator();
563 if (accelerator != null)
564 focusedWindowMap.put(accelerator, "doClick");
565 SwingUtilities.replaceUIInputMap(menuItem,
566 JComponent.WHEN_IN_FOCUSED_WINDOW, focusedWindowMap);
568 ActionMap UIActionMap = SwingUtilities.getUIActionMap(menuItem);
569 if (UIActionMap == null)
570 UIActionMap = new ActionMapUIResource();
571 UIActionMap.put("doClick", new ClickAction());
572 SwingUtilities.replaceUIActionMap(menuItem, UIActionMap);
576 * This method installs the listeners for the {@link JMenuItem}.
578 protected void installListeners()
580 menuItem.addMouseListener(mouseInputListener);
581 menuItem.addMouseMotionListener(mouseInputListener);
582 menuItem.addMenuDragMouseListener(menuDragMouseListener);
583 menuItem.addMenuKeyListener(menuKeyListener);
584 menuItem.addItemListener(itemListener);
585 menuItem.addPropertyChangeListener(propertyChangeListener);
589 * Installs and initializes all fields for this UI delegate. Any properties of
590 * the UI that need to be initialized and/or set to defaults will be done now.
591 * It will also install any listeners necessary.
594 * The {@link JComponent} that is having this UI installed.
596 public void installUI(JComponent c)
599 menuItem = (JMenuItem) c;
601 installComponents(menuItem);
603 installKeyboardActions();
607 * Paints given menu item using specified graphics context
610 * The graphics context used to paint this menu item
614 public void paint(Graphics g, JComponent c)
616 paintMenuItem(g, c, checkIcon, arrowIcon, selectionBackground,
617 c.getForeground(), defaultTextIconGap);
621 * Paints background of the menu item
624 * The graphics context used to paint this menu item
628 * Background color to use when painting menu item
630 protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor)
632 // Menu item is considered to be highlighted when it is selected.
633 // But we don't want to paint the background of JCheckBoxMenuItems
634 ButtonModel mod = menuItem.getModel();
635 Color saved = g.getColor();
636 if (mod.isArmed() || ((menuItem instanceof JMenu) && mod.isSelected()))
639 g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
641 else if (menuItem.isOpaque())
643 g.setColor(menuItem.getBackground());
644 g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
650 * Paints specified menu item
653 * The graphics context used to paint this menu item
657 * check icon to use when painting menu item
659 * arrow icon to use when painting menu item
661 * Background color of the menu item
663 * Foreground color of the menu item
664 * @param defaultTextIconGap
665 * space to use between icon and text when painting menu item
667 protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
668 Icon arrowIcon, Color background,
669 Color foreground, int defaultTextIconGap)
671 JMenuItem m = (JMenuItem) c;
674 Font oldFont = g.getFont();
675 Font font = c.getFont();
677 FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
679 // Create accelerator string.
680 String accelText = getAcceleratorString(m);
682 // Layout menu item. The result gets stored in the rectangle fields
684 layoutMenuItem(m, accelText);
686 // Paint the background.
687 paintBackground(g, m, background);
689 Color oldColor = g.getColor();
691 // Paint the check icon.
692 if (checkIcon != null)
694 checkIcon.paintIcon(m, g, checkIconRect.x, checkIconRect.y);
698 ButtonModel model = m.getModel();
699 if (m.getIcon() != null)
701 // Determine icon depending on the menu item
702 // state (normal/disabled/pressed).
706 icon = m.getDisabledIcon();
708 else if (model.isPressed() && model.isArmed())
710 icon = m.getPressedIcon();
723 icon.paintIcon(m, g, iconRect.x, iconRect.y);
728 String text = m.getText();
732 View html = (View) m.getClientProperty(BasicHTML.propertyKey);
735 html.paint(g, textRect);
739 paintText(g, m, textRect, text);
743 // Paint accelerator text.
744 if (! accelText.equals(""))
746 // Align the accelerator text. In getPreferredMenuItemSize() we
747 // store a client property 'maxAccelWidth' in the parent which holds
748 // the maximum accelerator width for the children of this parent.
749 // We use this here to align the accelerators properly.
751 Container parent = m.getParent();
752 if (parent != null && parent instanceof JComponent)
754 JComponent p = (JComponent) parent;
755 Integer maxAccelWidth =
756 (Integer) p.getClientProperty("maxAccelWidth");
757 int maxAccelValue = maxAccelWidth == null ? 0
758 : maxAccelWidth.intValue();
759 accelOffset = maxAccelValue - accelRect.width;
762 g.setFont(acceleratorFont);
765 // Paint accelerator disabled.
766 g.setColor(disabledForeground);
770 if (m.isArmed() || (m instanceof JMenu && m.isSelected()))
771 g.setColor(acceleratorSelectionForeground);
773 g.setColor(acceleratorForeground);
775 g.drawString(accelText, accelRect.x - accelOffset,
776 accelRect.y + accelFm.getAscent());
780 if (arrowIcon != null
781 && ! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
783 arrowIcon.paintIcon(m, g, arrowIconRect.x, arrowIconRect.y);
787 g.setColor(oldColor);
792 * Paints label for the given menu item
795 * The graphics context used to paint this menu item
797 * menu item for which to draw its label
799 * rectangle specifiying position of the text relative to the given
802 * label of the menu item
804 protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect,
807 Font f = menuItem.getFont();
809 FontMetrics fm = g.getFontMetrics(f);
811 if (text != null && !text.equals(""))
813 if (menuItem.isEnabled())
815 // Menu item is considered to be highlighted when it is selected.
816 // But not if it's a JCheckBoxMenuItem
817 ButtonModel mod = menuItem.getModel();
818 if ((menuItem.isSelected() && checkIcon == null)
819 || (mod != null && mod.isArmed())
820 && (menuItem.getParent() instanceof MenuElement))
821 g.setColor(selectionForeground);
823 g.setColor(menuItem.getForeground());
826 // FIXME: should fix this to use 'disabledForeground', but its
827 // default value in BasicLookAndFeel is null.
829 // FIXME: should there be different foreground colours for selected
830 // or deselected, when disabled?
831 g.setColor(Color.gray);
833 int mnemonicIndex = menuItem.getDisplayedMnemonicIndex();
835 if (mnemonicIndex != -1)
836 BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex,
841 BasicGraphicsUtils.drawString(g, text, 0, textRect.x,
842 textRect.y + fm.getAscent());
847 * This method uninstalls the components for this {@link JMenuItem}.
850 * The {@link JMenuItem} to uninstall components for.
852 protected void uninstallComponents(JMenuItem menuItem)
854 // FIXME: need to implement
858 * This method uninstalls the defaults and sets any objects created during
861 protected void uninstallDefaults()
863 menuItem.setForeground(null);
864 menuItem.setBackground(null);
865 menuItem.setBorder(null);
866 menuItem.setMargin(null);
867 menuItem.setBackground(null);
868 menuItem.setBorder(null);
869 menuItem.setFont(null);
870 menuItem.setForeground(null);
871 menuItem.setMargin(null);
872 acceleratorFont = null;
873 acceleratorForeground = null;
874 acceleratorSelectionForeground = null;
876 selectionBackground = null;
877 selectionForeground = null;
878 acceleratorDelimiter = null;
882 * Uninstalls any keyboard actions.
884 protected void uninstallKeyboardActions()
886 SwingUtilities.replaceUIInputMap(menuItem,
887 JComponent.WHEN_IN_FOCUSED_WINDOW, null);
891 * Unregisters all the listeners that this UI delegate was using.
893 protected void uninstallListeners()
895 menuItem.removeMouseListener(mouseInputListener);
896 menuItem.removeMenuDragMouseListener(menuDragMouseListener);
897 menuItem.removeMenuKeyListener(menuKeyListener);
898 menuItem.removeItemListener(itemListener);
899 menuItem.removePropertyChangeListener(propertyChangeListener);
903 * Performs the opposite of installUI. Any properties or resources that need
904 * to be cleaned up will be done now. It will also uninstall any listeners it
905 * has. In addition, any properties of this UI will be nulled.
908 * The {@link JComponent} that is having this UI uninstalled.
910 public void uninstallUI(JComponent c)
912 uninstallListeners();
914 uninstallComponents(menuItem);
919 * This method calls paint.
922 * The graphics context used to paint this menu item
924 * The menu item to paint
926 public void update(Graphics g, JComponent c)
932 * Return text representation of the specified accelerator
935 * Accelerator for which to return string representation
936 * @return $String$ Text representation of the given accelerator
938 private String getAcceleratorText(KeyStroke accelerator)
940 // convert keystroke into string format
941 String modifiersText = "";
942 int modifiers = accelerator.getModifiers();
943 char keyChar = accelerator.getKeyChar();
944 int keyCode = accelerator.getKeyCode();
947 modifiersText = KeyEvent.getKeyModifiersText(modifiers)
948 + acceleratorDelimiter;
950 if (keyCode == KeyEvent.VK_UNDEFINED)
951 return modifiersText + keyChar;
953 return modifiersText + KeyEvent.getKeyText(keyCode);
957 * Calculates and return rectange in which accelerator should be displayed
960 * accelerator for which to return the display rectangle
962 * The font metrics used to measure the text
963 * @return $Rectangle$ reactangle which will be used to display accelerator
965 private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm)
967 int width = fm.stringWidth(getAcceleratorText(accelerator));
968 int height = fm.getHeight();
969 return new Rectangle(0, 0, width, height);
973 * This class handles mouse events occuring inside the menu item. Most of the
974 * events are forwarded for processing to MenuSelectionManager of the current
977 protected class MouseInputHandler implements MouseInputListener
980 * Creates a new MouseInputHandler object.
982 protected MouseInputHandler()
984 // Nothing to do here.
988 * This method is called when mouse is clicked on the menu item. It forwards
989 * this event to MenuSelectionManager.
992 * A {@link MouseEvent}.
994 public void mouseClicked(MouseEvent e)
996 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
997 manager.processMouseEvent(e);
1001 * This method is called when mouse is dragged inside the menu item. It
1002 * forwards this event to MenuSelectionManager.
1005 * A {@link MouseEvent}.
1007 public void mouseDragged(MouseEvent e)
1009 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1010 manager.processMouseEvent(e);
1014 * This method is called when mouse enters menu item. When this happens menu
1015 * item is considered to be selected and selection path in
1016 * MenuSelectionManager is set. This event is also forwarded to
1017 * MenuSelection Manager for further processing.
1020 * A {@link MouseEvent}.
1022 public void mouseEntered(MouseEvent e)
1024 Component source = (Component) e.getSource();
1025 if (source.getParent() instanceof MenuElement)
1027 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1028 manager.setSelectedPath(getPath());
1029 manager.processMouseEvent(e);
1034 * This method is called when mouse exits menu item. The event is forwarded
1035 * to MenuSelectionManager for processing.
1038 * A {@link MouseEvent}.
1040 public void mouseExited(MouseEvent e)
1042 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1043 manager.processMouseEvent(e);
1047 * This method is called when mouse is inside the menu item. This event is
1048 * forwarder to MenuSelectionManager for further processing.
1051 * A {@link MouseEvent}.
1053 public void mouseMoved(MouseEvent e)
1055 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1056 manager.processMouseEvent(e);
1060 * This method is called when mouse is pressed. This event is forwarded to
1061 * MenuSelectionManager for further processing.
1064 * A {@link MouseEvent}.
1066 public void mousePressed(MouseEvent e)
1068 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1069 manager.processMouseEvent(e);
1073 * This method is called when mouse is released. If the mouse is released
1074 * inside this menuItem, then this menu item is considered to be chosen and
1075 * the menu hierarchy should be closed.
1078 * A {@link MouseEvent}.
1080 public void mouseReleased(MouseEvent e)
1082 Rectangle size = menuItem.getBounds();
1083 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1084 if (e.getX() > 0 && e.getX() < size.width && e.getY() > 0
1085 && e.getY() < size.height)
1087 manager.clearSelectedPath();
1092 manager.processMouseEvent(e);
1097 * This class handles mouse dragged events.
1099 private class MenuDragMouseHandler implements MenuDragMouseListener
1102 * Tbis method is invoked when mouse is dragged over the menu item.
1105 * The MenuDragMouseEvent
1107 public void menuDragMouseDragged(MenuDragMouseEvent e)
1109 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1110 manager.setSelectedPath(e.getPath());
1114 * Tbis method is invoked when mouse enters the menu item while it is being
1118 * The MenuDragMouseEvent
1120 public void menuDragMouseEntered(MenuDragMouseEvent e)
1122 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1123 manager.setSelectedPath(e.getPath());
1127 * Tbis method is invoked when mouse exits the menu item while it is being
1130 * @param e the MenuDragMouseEvent
1132 public void menuDragMouseExited(MenuDragMouseEvent e)
1134 // TODO: What should be done here, if anything?
1138 * Tbis method is invoked when mouse was dragged and released inside the
1142 * The MenuDragMouseEvent
1144 public void menuDragMouseReleased(MenuDragMouseEvent e)
1146 MenuElement[] path = e.getPath();
1148 if (path[path.length - 1] instanceof JMenuItem)
1149 ((JMenuItem) path[path.length - 1]).doClick();
1151 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1152 manager.clearSelectedPath();
1157 * This class handles key events occuring when menu item is visible on the
1160 private class MenuKeyHandler implements MenuKeyListener
1163 * This method is invoked when key has been pressed
1166 * A {@link MenuKeyEvent}.
1168 public void menuKeyPressed(MenuKeyEvent e)
1170 // TODO: What should be done here, if anything?
1174 * This method is invoked when key has been pressed
1177 * A {@link MenuKeyEvent}.
1179 public void menuKeyReleased(MenuKeyEvent e)
1181 // TODO: What should be done here, if anything?
1185 * This method is invoked when key has been typed It handles the mnemonic
1186 * key for the menu item.
1189 * A {@link MenuKeyEvent}.
1191 public void menuKeyTyped(MenuKeyEvent e)
1193 // TODO: What should be done here, if anything?
1198 * Helper class that listens for item changes to the properties of the {@link
1201 private class ItemHandler implements ItemListener
1204 * This method is called when one of the menu item changes.
1206 * @param evt A {@link ItemEvent}.
1208 public void itemStateChanged(ItemEvent evt)
1210 boolean state = false;
1211 if (menuItem instanceof JCheckBoxMenuItem)
1213 if (evt.getStateChange() == ItemEvent.SELECTED)
1215 ((JCheckBoxMenuItem) menuItem).setState(state);
1217 menuItem.revalidate();
1223 * A helper method to create the accelerator string from the menu item's
1224 * accelerator property. The returned string is empty if there is
1225 * no accelerator defined.
1227 * @param m the menu item
1229 * @return the accelerator string, not null
1231 private String getAcceleratorString(JMenuItem m)
1233 // Create accelerator string.
1234 KeyStroke accel = m.getAccelerator();
1235 String accelText = "";
1238 int mods = accel.getModifiers();
1241 accelText = KeyEvent.getKeyModifiersText(mods);
1242 accelText += acceleratorDelimiter;
1244 int keycode = accel.getKeyCode();
1246 accelText += KeyEvent.getKeyText(keycode);
1248 accelText += accel.getKeyChar();
1254 * A helper method that lays out the menu item. The layout is stored
1255 * in the fields of this class.
1257 * @param m the menu item to layout
1258 * @param accelText the accelerator text
1260 private void layoutMenuItem(JMenuItem m, String accelText)
1262 int width = m.getWidth();
1263 int height = m.getHeight();
1265 // Reset rectangles.
1266 iconRect.setBounds(0, 0, 0, 0);
1267 textRect.setBounds(0, 0, 0, 0);
1268 accelRect.setBounds(0, 0, 0, 0);
1269 checkIconRect.setBounds(0, 0, 0, 0);
1270 arrowIconRect.setBounds(0, 0, 0, 0);
1271 viewRect.setBounds(0, 0, width, height);
1273 // Substract insets to the view rect.
1274 Insets insets = m.getInsets();
1275 viewRect.x += insets.left;
1276 viewRect.y += insets.top;
1277 viewRect.width -= insets.left + insets.right;
1278 viewRect.height -= insets.top + insets.bottom;
1281 Font font = m.getFont();
1282 FontMetrics fm = m.getFontMetrics(font);
1283 FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
1285 String text = m.getText();
1286 SwingUtilities.layoutCompoundLabel(m, fm, text, m.getIcon(),
1287 m.getVerticalAlignment(),
1288 m.getHorizontalAlignment(),
1289 m.getVerticalTextPosition(),
1290 m.getHorizontalTextPosition(),
1291 viewRect, iconRect, textRect,
1292 defaultTextIconGap);
1294 // Initialize accelerator width and height.
1295 if (! accelText.equals(""))
1297 accelRect.width = accelFm.stringWidth(accelText);
1298 accelRect.height = accelFm.getHeight();
1301 // Initialize check and arrow icon width and height.
1302 if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
1304 if (checkIcon != null)
1306 checkIconRect.width = checkIcon.getIconWidth();
1307 checkIconRect.height = checkIcon.getIconHeight();
1309 if (arrowIcon != null)
1311 arrowIconRect.width = arrowIcon.getIconWidth();
1312 arrowIconRect.height = arrowIcon.getIconHeight();
1316 // The union of the icon and text of the menu item is the 'label area'.
1317 cachedRect.setBounds(textRect);
1318 Rectangle labelRect = SwingUtilities.computeUnion(iconRect.x,
1323 textRect.x += defaultTextIconGap;
1324 iconRect.x += defaultTextIconGap;
1326 // Layout accelerator rect.
1327 accelRect.x = viewRect.x + viewRect.width - arrowIconRect.width
1328 - defaultTextIconGap - accelRect.width;
1329 // Layout check and arrow icons only when not in toplevel menu.
1330 if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
1332 checkIconRect.x = viewRect.x + defaultTextIconGap;
1333 textRect.x += defaultTextIconGap + checkIconRect.width;
1334 iconRect.x += defaultTextIconGap + checkIconRect.width;
1335 arrowIconRect.x = viewRect.x + viewRect.width - defaultTextIconGap
1336 - arrowIconRect.width;
1339 // Align the accelerator text and all the icons vertically centered to
1341 accelRect.y = labelRect.y + (labelRect.height / 2)
1342 - (accelRect.height / 2);
1343 if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
1345 arrowIconRect.y = labelRect.y + (labelRect.height / 2)
1346 - (arrowIconRect.height / 2);
1347 checkIconRect.y = labelRect.y + (labelRect.height / 2)
1348 - (checkIconRect.height / 2);