OSDN Git Service

Initial revision
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / plaf / basic / BasicMenuItemUI.java
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
new file mode 100644 (file)
index 0000000..a5bf082
--- /dev/null
@@ -0,0 +1,1006 @@
+/* BasicMenuItemUI.java --
+   Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.KeyStroke;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.event.MenuDragMouseEvent;
+import javax.swing.event.MenuDragMouseListener;
+import javax.swing.event.MenuKeyEvent;
+import javax.swing.event.MenuKeyListener;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.MenuItemUI;
+
+/**
+ * UI Delegate for JMenuItem.
+ */
+public class BasicMenuItemUI extends MenuItemUI
+{
+  /**
+   * Font to be used when displaying menu item's accelerator.
+   */
+  protected Font acceleratorFont;
+
+  /**
+   * Color to be used when displaying menu item's accelerator.
+   */
+  protected Color acceleratorForeground;
+
+  /**
+   * Color to be used when displaying menu item's accelerator when menu item is
+   * selected.
+   */
+  protected Color acceleratorSelectionForeground;
+
+  /**
+   * Icon that is displayed after the text to indicated that this menu contains
+   * submenu.
+   */
+  protected Icon arrowIcon;
+
+  /**
+   * Icon that is displayed before the text. This icon is only used in
+   * JCheckBoxMenuItem or JRadioBoxMenuItem.
+   */
+  protected Icon checkIcon;
+
+  /**
+   * Number of spaces between icon and text.
+   */
+  protected int defaultTextIconGap = 4;
+
+  /**
+   * Color of the text when menu item is disabled
+   */
+  protected Color disabledForeground;
+
+  /**
+   * The menu Drag mouse listener listening to the menu item.
+   */
+  protected MenuDragMouseListener menuDragMouseListener;
+
+  /**
+   * The menu item itself
+   */
+  protected JMenuItem menuItem;
+
+  /**
+   * Menu Key listener listening to the menu item.
+   */
+  protected MenuKeyListener menuKeyListener;
+
+  /**
+   * mouse input listener listening to menu item.
+   */
+  protected MouseInputListener mouseInputListener;
+
+  /**
+   * Indicates if border should be painted
+   */
+  protected boolean oldBorderPainted;
+
+  /**
+   * Color of text that is used when menu item is selected
+   */
+  protected Color selectionBackground;
+
+  /**
+   * Color of the text that is used when menu item is selected.
+   */
+  protected Color selectionForeground;
+
+  /**
+   * String that separates description of the modifiers and the key
+   */
+  private String acceleratorDelimiter;
+
+  /**
+   * PropertyChangeListener to listen for property changes in the menu item
+   */
+  private PropertyChangeListener propertyChangeListener;
+
+  /**
+   * Number of spaces between accelerator and menu item's label.
+   */
+  private int defaultAcceleratorLabelGap = 4;
+
+  /**
+   * Creates a new BasicMenuItemUI object.
+   */
+  public BasicMenuItemUI()
+  {
+    mouseInputListener = createMouseInputListener(menuItem);
+    menuDragMouseListener = createMenuDragMouseListener(menuItem);
+    menuKeyListener = createMenuKeyListener(menuItem);
+    propertyChangeListener = new PropertyChangeHandler();
+  }
+
+  /**
+   * Create MenuDragMouseListener to listen for mouse dragged events.
+   *
+   * @param c menu item to listen to
+   *
+   * @return The MenuDragMouseListener
+   */
+  protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
+  {
+    return new MenuDragMouseHandler();
+  }
+
+  /**
+   * Creates MenuKeyListener to listen to key events occuring when menu item
+   * is visible on the screen.
+   *
+   * @param c menu item to listen to
+   *
+   * @return The MenuKeyListener
+   */
+  protected MenuKeyListener createMenuKeyListener(JComponent c)
+  {
+    return new MenuKeyHandler();
+  }
+
+  /**
+   * Handles mouse input events occuring for this menu item
+   *
+   * @param c menu item to listen to
+   *
+   * @return The MouseInputListener
+   */
+  protected MouseInputListener createMouseInputListener(JComponent c)
+  {
+    return new MouseInputHandler();
+  }
+
+  /**
+   * Factory method to create a BasicMenuItemUI for the given {@link
+   * JComponent}, which should be a {@link JMenuItem}.
+   *
+   * @param c The {@link JComponent} a UI is being created for.
+   *
+   * @return A BasicMenuItemUI for the {@link JComponent}.
+   */
+  public static ComponentUI createUI(JComponent c)
+  {
+    return new BasicMenuItemUI();
+  }
+
+  /**
+   * Programatically clicks menu item.
+   *
+   * @param msm MenuSelectionManager for the menu hierarchy
+   */
+  protected void doClick(MenuSelectionManager msm)
+  {
+    menuItem.doClick();
+    msm.clearSelectedPath();
+  }
+
+  /**
+   * Returns maximum size for the specified menu item
+   *
+   * @param c component for which to get maximum size
+   *
+   * @return Maximum size for the specified menu item.
+   */
+  public Dimension getMaximumSize(JComponent c)
+  {
+    return null;
+  }
+
+  /**
+   * Returns minimum size for the specified menu item
+   *
+   * @param c component for which to get minimum size
+   *
+   * @return Minimum size for the specified menu item.
+   */
+  public Dimension getMinimumSize(JComponent c)
+  {
+    return null;
+  }
+
+  /**
+   * Returns path to this menu item.
+   *
+   * @return $MenuElement[]$ Returns array of menu elements
+   * that constitute a path to this menu item.
+   */
+  public MenuElement[] getPath()
+  {
+    ArrayList path = new ArrayList();
+
+    // Path to menu should also include its popup menu.
+    if (menuItem instanceof JMenu)
+      path.add(((JMenu) menuItem).getPopupMenu());
+
+    Component c = menuItem;
+    while (c instanceof MenuElement)
+      {
+       path.add(0, (MenuElement) c);
+
+       if (c instanceof JPopupMenu)
+         c = ((JPopupMenu) c).getInvoker();
+       else
+         c = c.getParent();
+      }
+
+    MenuElement[] pathArray = new MenuElement[path.size()];
+    path.toArray(pathArray);
+    return pathArray;
+  }
+
+  /**
+   * Returns preferred size for the given menu item.
+   *
+   * @param c menu item for which to get preferred size
+   * @param checkIcon chech icon displayed in the given menu item
+   * @param arrowIcon arrow icon displayed in the given menu item
+   * @param defaultTextIconGap space between icon and text in the given menuItem
+   *
+   * @return $Dimension$ preferred size for the given menu item
+   */
+  protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon,
+                                               Icon arrowIcon,
+                                               int defaultTextIconGap)
+  {
+    JMenuItem m = (JMenuItem) c;
+    Dimension d = BasicGraphicsUtils.getPreferredButtonSize(m,
+                                                            defaultTextIconGap);
+
+    // if menu item has accelerator then take accelerator's size into account
+    // when calculating preferred size.
+    KeyStroke accelerator = m.getAccelerator();
+    Rectangle rect;
+
+    if (accelerator != null)
+      {
+       rect = getAcceleratorRect(accelerator,
+                                 m.getToolkit().getFontMetrics(acceleratorFont));
+
+       // add width of accelerator's text
+       d.width = d.width + rect.width + defaultAcceleratorLabelGap;
+
+       // adjust the heigth of the preferred size if necessary
+       if (d.height < rect.height)
+         d.height = rect.height;
+      }
+
+    if (checkIcon != null)
+      {
+       d.width = d.width + checkIcon.getIconWidth() + defaultTextIconGap;
+
+       if (checkIcon.getIconHeight() > d.height)
+         d.height = checkIcon.getIconHeight();
+      }
+
+    if (arrowIcon != null && (c instanceof JMenu))
+      {
+       d.width = d.width + arrowIcon.getIconWidth() + defaultTextIconGap;
+
+       if (arrowIcon.getIconHeight() > d.height)
+         d.height = arrowIcon.getIconHeight();
+      }
+
+    return d;
+  }
+
+  /**
+   * Returns preferred size of the given component
+   *
+   * @param c component for which to return preferred size
+   *
+   * @return $Dimension$ preferred size for the given component
+   */
+  public Dimension getPreferredSize(JComponent c)
+  {
+    return getPreferredMenuItemSize(c, checkIcon, arrowIcon, defaultTextIconGap);
+  }
+
+  protected String getPropertyPrefix()
+  {
+    return null;
+  }
+
+  /**
+   * This method installs the components for this {@link JMenuItem}.
+   *
+   * @param menuItem The {@link JMenuItem} to install components for.
+   */
+  protected void installComponents(JMenuItem menuItem)
+  {
+    // FIXME: Need to implement
+  }
+
+  /**
+   * This method installs the defaults that are defined in  the Basic look and
+   * feel for this {@link JMenuItem}.
+   */
+  protected void installDefaults()
+  {
+    UIDefaults defaults = UIManager.getLookAndFeelDefaults();
+
+    menuItem.setBackground(defaults.getColor("MenuItem.background"));
+    menuItem.setBorder(defaults.getBorder("MenuItem.border"));
+    menuItem.setFont(defaults.getFont("MenuItem.font"));
+    menuItem.setForeground(defaults.getColor("MenuItem.foreground"));
+    menuItem.setMargin(defaults.getInsets("MenuItem.margin"));
+    menuItem.setOpaque(true);
+    acceleratorFont = defaults.getFont("MenuItem.acceleratorFont");
+    acceleratorForeground = defaults.getColor("MenuItem.acceleratorForeground");
+    acceleratorSelectionForeground = defaults.getColor("MenuItem.acceleratorSelectionForeground");
+    selectionBackground = defaults.getColor("MenuItem.selectionBackground");
+    selectionForeground = defaults.getColor("MenuItem.selectionForeground");
+    acceleratorDelimiter = defaults.getString("MenuItem.acceleratorDelimiter");
+
+    menuItem.setHorizontalTextPosition(SwingConstants.TRAILING);
+    menuItem.setHorizontalAlignment(SwingConstants.LEADING);
+  }
+
+  /**
+   * This method installs the keyboard actions for this {@link JMenuItem}.
+   */
+  protected void installKeyboardActions()
+  {
+    // FIXME: Need to implement
+  }
+
+  /**
+   * This method installs the listeners for the {@link JMenuItem}.
+   */
+  protected void installListeners()
+  {
+    menuItem.addMouseListener(mouseInputListener);
+    menuItem.addMouseMotionListener(mouseInputListener);
+    menuItem.addMenuDragMouseListener(menuDragMouseListener);
+    menuItem.addMenuKeyListener(menuKeyListener);
+    menuItem.addPropertyChangeListener(propertyChangeListener);
+  }
+
+  /**
+   * Installs and initializes all fields for this UI delegate. Any properties
+   * of the UI that need to be initialized and/or set to defaults will be
+   * done now. It will also install any listeners necessary.
+   *
+   * @param c The {@link JComponent} that is having this UI installed.
+   */
+  public void installUI(JComponent c)
+  {
+    super.installUI(c);
+    menuItem = (JMenuItem) c;
+    installDefaults();
+    installComponents(menuItem);
+    installListeners();
+  }
+
+  /**
+   * Paints given menu item using specified graphics context
+   *
+   * @param g The graphics context used to paint this menu item
+   * @param c Menu Item to paint
+   */
+  public void paint(Graphics g, JComponent c)
+  {
+    paintMenuItem(g, c, checkIcon, arrowIcon, c.getBackground(),
+                  c.getForeground(), defaultTextIconGap);
+  }
+
+  /**
+   * Paints background of the menu item
+   *
+   * @param g The graphics context used to paint this menu item
+   * @param menuItem menu item to paint
+   * @param bgColor Background color to use when painting menu item
+   */
+  protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor)
+  {
+    Dimension size = getPreferredSize(menuItem);
+    Color foreground = g.getColor();
+    g.setColor(bgColor);
+    g.drawRect(0, 0, size.width, size.height);
+    g.setColor(foreground);
+  }
+
+  /**
+   * Paints specified menu item
+   *
+   * @param g The graphics context used to paint this menu item
+   * @param c menu item to paint
+   * @param checkIcon check icon to use when painting menu item
+   * @param arrowIcon arrow icon to use when painting menu item
+   * @param background Background color of the menu item
+   * @param foreground Foreground color of the menu item
+   * @param defaultTextIconGap space to use between icon and
+   *  text when painting menu item
+   */
+  protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
+                               Icon arrowIcon, Color background,
+                               Color foreground, int defaultTextIconGap)
+  {
+    JMenuItem m = (JMenuItem) c;
+    Rectangle tr = new Rectangle(); // text rectangle
+    Rectangle ir = new Rectangle(); // icon rectangle
+    Rectangle vr = new Rectangle(); // view rectangle
+    Rectangle br = new Rectangle(); // border rectangle
+    Rectangle ar = new Rectangle(); // accelerator rectangle
+    Rectangle cr = new Rectangle(); // checkIcon rectangle
+
+    int vertAlign = m.getVerticalAlignment();
+    int horAlign = m.getHorizontalAlignment();
+    int vertTextPos = m.getVerticalTextPosition();
+    int horTextPos = m.getHorizontalTextPosition();
+
+    Font f = m.getFont();
+    g.setFont(f);
+    FontMetrics fm = g.getFontMetrics(f);
+    SwingUtilities.calculateInnerArea(m, br);
+    SwingUtilities.calculateInsetArea(br, m.getInsets(), vr);
+    paintBackground(g, m, m.getBackground());
+
+    /* MenuItems insets are equal to menuItems margin, space between text and
+       menuItems border. We need to paint insets region as well. */
+    Insets insets = m.getInsets();
+    br.x -= insets.left;
+    br.y -= insets.top;
+    br.width += insets.right + insets.left;
+    br.height += insets.top + insets.bottom;
+
+    // Menu item is considered to be highlighted when it is selected.
+    if (m.isSelected() || m.getModel().isArmed() && 
+        (m.getParent() instanceof MenuElement)) 
+      {
+       if (m.isContentAreaFilled())
+         {
+           g.setColor(selectionBackground);
+           g.fillRect(br.x, br.y, br.width, br.height);
+         }
+      }
+    else
+      {
+       if (m.isContentAreaFilled())
+         {
+           g.setColor(m.getBackground());
+           g.fillRect(br.x, br.y, br.width, br.height);
+         }
+      }
+
+    // If this menu item is a JCheckBoxMenuItem then paint check icon
+    if (checkIcon != null)
+      {
+       SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign,
+                                          horAlign, vertTextPos, horTextPos,
+                                          vr, cr, tr, defaultTextIconGap);
+       checkIcon.paintIcon(m, g, cr.x, cr.y);
+
+       // We need to calculate position of the menu text and position of
+       // user menu icon if there exists one relative to the check icon.
+       // So we need to adjust view rectangle s.t. its starting point is at
+       // checkIcon.width + defaultTextIconGap. 
+       vr.x = cr.x + cr.width + defaultTextIconGap;
+      }
+
+    // if this is a submenu, then paint arrow icon to indicate it.
+    if (arrowIcon != null && (c instanceof JMenu))
+      {
+       if (! ((JMenu) c).isTopLevelMenu())
+         {
+           int width = arrowIcon.getIconWidth();
+           int height = arrowIcon.getIconHeight();
+
+           arrowIcon.paintIcon(m, g, vr.width - width + defaultTextIconGap,
+                               vr.y + 2);
+         }
+      }
+
+    // paint text and user menu icon if it exists           
+    Icon i = m.getIcon();
+    SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i,
+                                       vertAlign, horAlign, vertTextPos,
+                                       horTextPos, vr, ir, tr,
+                                       defaultTextIconGap);
+    if (i != null)
+      i.paintIcon(c, g, ir.x, ir.y);
+
+    paintText(g, m, tr, m.getText());
+
+    // paint accelerator    
+    String acceleratorText = "";
+
+    if (m.getAccelerator() != null)
+      {
+       acceleratorText = getAcceleratorText(m.getAccelerator());
+       fm = g.getFontMetrics(acceleratorFont);
+       ar.width = fm.stringWidth(acceleratorText);
+       ar.x = br.width - ar.width;
+       vr.x = br.width - ar.width;
+
+       SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null,
+                                          vertAlign, horAlign, vertTextPos,
+                                          horTextPos, vr, ir, ar,
+                                          defaultTextIconGap);
+
+       paintAccelerator(g, m, ar, acceleratorText);
+      }
+  }
+
+  /**
+   * Paints label for the given menu item
+   *
+   * @param g The graphics context used to paint this menu item
+   * @param menuItem menu item for which to draw its label
+   * @param textRect rectangle specifiying position of the text relative to
+   * the given menu item
+   * @param text label of the menu item
+   */
+  protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect,
+                           String text)
+  {
+    Font f = menuItem.getFont();
+    g.setFont(f);
+    FontMetrics fm = g.getFontMetrics(f);
+
+    if (text != null && ! text.equals(""))
+      {
+       if (menuItem.isEnabled())
+          {
+            // Menu item is considered to be highlighted when it is selected.
+            if (menuItem.isSelected() || menuItem.getModel().isArmed() && 
+                (menuItem.getParent() instanceof MenuElement)) 
+              g.setColor(selectionForeground);
+            else
+              g.setColor(menuItem.getForeground());
+          }
+       else
+         // FIXME: should fix this to use 'disabledForeground', but its
+         // default value in BasicLookAndFeel is null.   
+          
+          // FIXME: should there be different foreground colours for selected
+          // or deselected, when disabled?
+          g.setColor(Color.gray);
+
+       int mnemonicIndex = menuItem.getDisplayedMnemonicIndex();
+
+       if (mnemonicIndex != -1)
+         BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex,
+                                                      textRect.x,
+                                                      textRect.y
+                                                      + fm.getAscent());
+       else
+         BasicGraphicsUtils.drawString(g, text, 0, textRect.x,
+                                       textRect.y + fm.getAscent());
+      }
+  }
+
+  /**
+   * This method uninstalls the components for this {@link JMenuItem}.
+   *
+   * @param menuItem The {@link JMenuItem} to uninstall components for.
+   */
+  protected void uninstallComponents(JMenuItem menuItem)
+  {
+    // FIXME: need to implement
+  }
+
+  /**
+   * This method uninstalls the defaults and sets any objects created during
+   * install to null
+   */
+  protected void uninstallDefaults()
+  {
+    menuItem.setForeground(null);
+    menuItem.setBackground(null);
+    menuItem.setBorder(null);
+    menuItem.setMargin(null);
+    menuItem.setBackground(null);
+    menuItem.setBorder(null);
+    menuItem.setFont(null);
+    menuItem.setForeground(null);
+    menuItem.setMargin(null);
+    acceleratorFont = null;
+    acceleratorForeground = null;
+    acceleratorSelectionForeground = null;
+    arrowIcon = null;
+    selectionBackground = null;
+    selectionForeground = null;
+    acceleratorDelimiter = null;
+  }
+
+  /**
+   * Uninstalls any keyboard actions.
+   */
+  protected void uninstallKeyboardActions()
+  {
+    // FIXME: need to implement
+  }
+
+  /**
+   * Unregisters all the listeners that this UI delegate was using.
+   */
+  protected void uninstallListeners()
+  {
+    menuItem.removeMouseListener(mouseInputListener);
+    menuItem.removeMenuDragMouseListener(menuDragMouseListener);
+    menuItem.removeMenuKeyListener(menuKeyListener);
+    menuItem.removePropertyChangeListener(propertyChangeListener);
+  }
+
+  /**
+   * Performs the opposite of installUI. Any properties or resources that need
+   * to be cleaned up will be done now. It will also uninstall any listeners
+   * it has. In addition, any properties of this UI will be nulled.
+   *
+   * @param c The {@link JComponent} that is having this UI uninstalled.
+   */
+  public void uninstallUI(JComponent c)
+  {
+    uninstallListeners();
+    uninstallDefaults();
+    uninstallComponents(menuItem);
+    menuItem = null;
+  }
+
+  /**
+   * This method calls paint.
+   *
+   * @param g The graphics context used to paint this menu item
+   * @param c The menu item to paint
+   */
+  public void update(Graphics g, JComponent c)
+  {
+    paint(g, c);
+  }
+
+  /**
+   * Return text representation of the specified accelerator
+   *
+   * @param accelerator Accelerator for which to return string representation
+   *
+   * @return $String$ Text representation of the given accelerator
+   */
+  private String getAcceleratorText(KeyStroke accelerator)
+  {
+    // convert keystroke into string format
+    String modifiersText = "";
+    int modifiers = accelerator.getModifiers();
+    char keyChar = accelerator.getKeyChar();
+    int keyCode = accelerator.getKeyCode();
+
+    if (modifiers != 0)
+      modifiersText = KeyEvent.getKeyModifiersText(modifiers)
+                      + acceleratorDelimiter;
+
+    if (keyCode == KeyEvent.VK_UNDEFINED)
+      return modifiersText + keyChar;
+    else
+      return modifiersText + KeyEvent.getKeyText(keyCode);
+  }
+
+  /**
+   * Calculates and return rectange in which accelerator should be displayed
+   *
+   * @param accelerator accelerator for which to return the display rectangle
+   * @param fm The font metrics used to measure the text
+   *
+   * @return $Rectangle$ reactangle which will be used to display accelerator
+   */
+  private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm)
+  {
+    int width = fm.stringWidth(getAcceleratorText(accelerator));
+    int height = fm.getHeight();
+    return new Rectangle(0, 0, width, height);
+  }
+
+  /**
+   * Paints accelerator inside menu item
+   *
+   * @param g The graphics context used to paint the border
+   * @param menuItem Menu item for which to draw accelerator
+   * @param acceleratorRect rectangle representing position
+   * of the accelerator relative to the menu item
+   * @param acceleratorText accelerator's text
+   */
+  private void paintAccelerator(Graphics g, JMenuItem menuItem,
+                                Rectangle acceleratorRect,
+                                String acceleratorText)
+  {
+    g.setFont(acceleratorFont);
+    FontMetrics fm = g.getFontMetrics(acceleratorFont);
+
+    if (menuItem.isEnabled())
+      g.setColor(acceleratorForeground);
+    else
+      // FIXME: should fix this to use 'disabledForeground', but its
+      // default value in BasicLookAndFeel is null.
+      g.setColor(Color.gray);
+
+    BasicGraphicsUtils.drawString(g, acceleratorText, 0, acceleratorRect.x,
+                                  acceleratorRect.y + fm.getAscent());
+  }
+
+  /**
+   * This class handles mouse events occuring inside the menu item.
+   * Most of the events are forwarded for processing to MenuSelectionManager
+   * of the current menu hierarchy.
+   *
+   */
+  protected class MouseInputHandler implements MouseInputListener
+  {
+    /**
+     * Creates a new MouseInputHandler object.
+     */
+    protected MouseInputHandler()
+    {
+    }
+
+    /**
+     * This method is called when mouse is clicked on the menu item.
+     * It forwards this event to MenuSelectionManager.
+     *
+     * @param e A {@link MouseEvent}.
+     */
+    public void mouseClicked(MouseEvent e)
+    {
+      MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+      manager.processMouseEvent(e);
+    }
+
+    /**
+     * This method is called when mouse is dragged inside the menu item.
+     * It forwards this event to MenuSelectionManager.
+     *
+     * @param e A {@link MouseEvent}.
+     */
+    public void mouseDragged(MouseEvent e)
+    {
+      MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+      manager.processMouseEvent(e);
+    }
+
+    /**
+     * This method is called when mouse enters menu item.
+     * When this happens menu item is considered to be selected and selection path
+     * in MenuSelectionManager is set. This event is also forwarded to MenuSelection
+     * Manager for further processing.
+     *
+     * @param e A {@link MouseEvent}.
+     */
+    public void mouseEntered(MouseEvent e)
+    {
+      Component source = (Component) e.getSource();
+      if (source.getParent() instanceof MenuElement)
+        {
+         MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+         manager.setSelectedPath(getPath());
+         manager.processMouseEvent(e);
+        }
+    }
+
+    /**
+     * This method is called when mouse exits menu item. The event is
+     * forwarded to MenuSelectionManager for processing.
+     *
+     * @param e A {@link MouseEvent}.
+     */
+    public void mouseExited(MouseEvent e)
+    {
+      MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+      manager.processMouseEvent(e);
+    }
+
+    /**
+     * This method is called when mouse is inside the menu item.
+     * This event is forwarder to MenuSelectionManager for further processing.
+     *
+     * @param e A {@link MouseEvent}.
+     */
+    public void mouseMoved(MouseEvent e)
+    {
+      MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+      manager.processMouseEvent(e);
+    }
+
+    /**
+     * This method is called when mouse is pressed. This event is forwarded to
+     * MenuSelectionManager for further processing.
+     *
+     * @param e A {@link MouseEvent}.
+     */
+    public void mousePressed(MouseEvent e)
+    {
+      MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+      manager.processMouseEvent(e);
+    }
+
+    /**
+     * This method is called when mouse is released. If the mouse is released
+     * inside this menuItem, then this menu item is considered to be chosen and
+     * the menu hierarchy should be closed.
+     *
+     * @param e A {@link MouseEvent}.
+     */
+    public void mouseReleased(MouseEvent e)
+    {
+      Rectangle size = menuItem.getBounds();
+      MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+      if (e.getX() > 0 && e.getX() < size.width && e.getY() > 0
+          && e.getY() < size.height)
+        {
+         manager.clearSelectedPath();
+         menuItem.doClick();
+        }
+
+      else
+       manager.processMouseEvent(e);
+    }
+  }
+
+  /**
+   * This class handles mouse dragged events.
+   */
+  protected class MenuDragMouseHandler implements MenuDragMouseListener
+  {
+    /**
+     * Tbis method is invoked when mouse is dragged over the menu item.
+     *
+     * @param e The MenuDragMouseEvent
+     */
+    public void menuDragMouseDragged(MenuDragMouseEvent e)
+    {
+      MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+      manager.setSelectedPath(e.getPath());
+    }
+
+    /**
+     * Tbis method is invoked when mouse enters the menu item while it is
+     * being dragged.
+     *
+     * @param e The MenuDragMouseEvent
+     */
+    public void menuDragMouseEntered(MenuDragMouseEvent e)
+    {
+      MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+      manager.setSelectedPath(e.getPath());
+    }
+
+    /**
+     * Tbis method is invoked when mouse exits the menu item while
+     * it is being dragged
+     *
+     * @param e The MenuDragMouseEvent
+     */
+    public void menuDragMouseExited(MenuDragMouseEvent e)
+    {
+    }
+
+    /**
+     * Tbis method is invoked when mouse was dragged and released
+     * inside the menu item.
+     *
+     * @param e The MenuDragMouseEvent
+     */
+    public void menuDragMouseReleased(MenuDragMouseEvent e)
+    {
+      MenuElement[] path = e.getPath();
+
+      if (path[path.length - 1] instanceof JMenuItem)
+       ((JMenuItem) path[path.length - 1]).doClick();
+
+      MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+      manager.clearSelectedPath();
+    }
+  }
+
+  /**
+   * This class handles key events occuring when menu item is visible on the
+   * screen.
+   */
+  protected class MenuKeyHandler implements MenuKeyListener
+  {
+    /**
+     * This method is invoked when key has been pressed
+     *
+     * @param e A {@link MenuKeyEvent}.
+     */
+    public void menuKeyPressed(MenuKeyEvent e)
+    {
+    }
+
+    /**
+     * This method is invoked when key has been pressed
+     *
+     * @param e A {@link MenuKeyEvent}.
+     */
+    public void menuKeyReleased(MenuKeyEvent e)
+    {
+    }
+
+    /**
+     * This method is invoked when key has been typed
+     * It handles the mnemonic key for the menu item.
+     *
+     * @param e A {@link MenuKeyEvent}.
+     */
+    public void menuKeyTyped(MenuKeyEvent e)
+    {
+    }
+  }
+
+  /**
+   * Helper class that listens for changes to the properties of the {@link
+   * JMenuItem}.
+   */
+  protected class PropertyChangeHandler implements PropertyChangeListener
+  {
+    /**
+     * This method is called when one of the menu item's properties change.
+     *
+     * @param evt A {@link PropertyChangeEvent}.
+     */
+    public void propertyChange(PropertyChangeEvent evt)
+    {
+      menuItem.revalidate();
+      menuItem.repaint();
+    }
+  }
+}