summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java')
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java1339
1 files changed, 1339 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
new file mode 100644
index 000000000..40b539378
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
@@ -0,0 +1,1339 @@
+/* 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 gnu.classpath.SystemProperties;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+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.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractButton;
+import javax.swing.ActionMap;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComponent;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+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.ActionMapUIResource;
+import javax.swing.plaf.ComponentInputMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.MenuItemUI;
+import javax.swing.text.View;
+
+/**
+ * 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;
+
+ /**
+ * ItemListener to listen for item changes in the menu item
+ */
+ private ItemListener itemListener;
+
+ /**
+ * A PropertyChangeListener to make UI updates after property changes.
+ */
+ private PropertyChangeHandler propertyChangeListener;
+
+ /**
+ * The view rectangle used for layout of the menu item.
+ */
+ private Rectangle viewRect;
+
+ /**
+ * The rectangle that holds the area of the label.
+ */
+ private Rectangle textRect;
+
+ /**
+ * The rectangle that holds the area of the accelerator.
+ */
+ private Rectangle accelRect;
+
+ /**
+ * The rectangle that holds the area of the icon.
+ */
+ private Rectangle iconRect;
+
+ /**
+ * The rectangle that holds the area of the icon.
+ */
+ private Rectangle arrowIconRect;
+
+ /**
+ * The rectangle that holds the area of the check icon.
+ */
+ private Rectangle checkIconRect;
+
+ /**
+ * A rectangle used for temporary storage to avoid creation of new
+ * rectangles.
+ */
+ private Rectangle cachedRect;
+
+ /**
+ * A class to handle PropertChangeEvents for the JMenuItem
+ * @author Anthony Balkissoon abalkiss at redhat dot com.
+ */
+ class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called when a property of the menuItem is changed.
+ * Currently it is only used to update the accelerator key bindings.
+ *
+ * @param e
+ * the PropertyChangeEvent
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String property = e.getPropertyName();
+ if (property.equals("accelerator"))
+ {
+ InputMap map = SwingUtilities.getUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ if (map != null)
+ map.remove((KeyStroke) e.getOldValue());
+ else
+ map = new ComponentInputMapUIResource(menuItem);
+
+ KeyStroke accelerator = (KeyStroke) e.getNewValue();
+ if (accelerator != null)
+ map.put(accelerator, "doClick");
+ }
+ // TextLayout caching for speed-up drawing of text.
+ else if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)
+ || property.equals("font"))
+ && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
+ == null)
+ {
+ AbstractButton b = (AbstractButton) e.getSource();
+ String text = b.getText();
+ if (text == null)
+ text = "";
+ FontRenderContext frc = new FontRenderContext(new AffineTransform(),
+ false, false);
+ TextLayout layout = new TextLayout(text, b.getFont(), frc);
+ b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout);
+ }
+ }
+ }
+
+ /**
+ * A class to handle accelerator keys. This is the Action we will
+ * perform when the accelerator key for this JMenuItem is pressed.
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ */
+ class ClickAction extends AbstractAction
+ {
+ /**
+ * This is what is done when the accelerator key for the JMenuItem is
+ * pressed.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ doClick(MenuSelectionManager.defaultManager());
+ }
+ }
+
+ /**
+ * Creates a new BasicMenuItemUI object.
+ */
+ public BasicMenuItemUI()
+ {
+ mouseInputListener = createMouseInputListener(menuItem);
+ menuDragMouseListener = createMenuDragMouseListener(menuItem);
+ menuKeyListener = createMenuKeyListener(menuItem);
+ itemListener = new ItemHandler();
+ propertyChangeListener = new PropertyChangeHandler();
+
+ // Initialize rectangles for layout.
+ viewRect = new Rectangle();
+ textRect = new Rectangle();
+ iconRect = new Rectangle();
+ arrowIconRect = new Rectangle();
+ checkIconRect = new Rectangle();
+ accelRect = new Rectangle();
+ cachedRect = new Rectangle();
+ }
+
+ /**
+ * 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(0);
+ 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();
+
+ Component c = menuItem;
+ while (c instanceof MenuElement)
+ {
+ path.add(0, 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
+ * check 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;
+ String accelText = getAcceleratorString(m);
+
+ // Layout the menu item. The result gets stored in the rectangle
+ // fields of this class.
+ resetRectangles(null);
+ layoutMenuItem(m, accelText);
+
+ // The union of the text and icon areas is the label area.
+ cachedRect.setBounds(textRect);
+ Rectangle pref = SwingUtilities.computeUnion(iconRect.x, iconRect.y,
+ iconRect.width,
+ iconRect.height,
+ cachedRect);
+
+ // Find the widest menu item text and accelerator and store it in
+ // client properties of the parent, so that we can align the accelerators
+ // properly. Of course, we only need can do this, if the parent is
+ // a JComponent and this menu item is not a toplevel menu.
+ Container parent = m.getParent();
+ if (parent != null && parent instanceof JComponent
+ && !(m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ JComponent p = (JComponent) parent;
+
+ // The widest text so far.
+ Integer maxTextWidth = (Integer) p.getClientProperty("maxTextWidth");
+ int maxTextValue = maxTextWidth == null ? 0 : maxTextWidth.intValue();
+ if (pref.width < maxTextValue)
+ pref.width = maxTextValue;
+ else
+ p.putClientProperty("maxTextWidth", new Integer(pref.width));
+
+ // The widest accelerator so far.
+ Integer maxAccelWidth = (Integer) p.getClientProperty("maxAccelWidth");
+ int maxAccelValue = maxAccelWidth == null ? 0
+ : maxAccelWidth.intValue();
+ if (accelRect.width > maxAccelValue)
+ {
+ maxAccelValue = accelRect.width;
+ p.putClientProperty("maxAccelWidth", new Integer(accelRect.width));
+ }
+ pref.width += maxAccelValue;
+ pref.width += defaultTextIconGap;
+ }
+
+ // Add arrow and check size if appropriate.
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ pref.width += checkIconRect.width;
+ pref.width += defaultTextIconGap;
+ pref.width += arrowIconRect.width;
+ pref.width += defaultTextIconGap;
+ }
+
+ // Add a gap ~2 times as wide as the defaultTextIconGap.
+ pref.width += 2 * defaultTextIconGap;
+
+ // Respect the insets of the menu item.
+ Insets i = m.getInsets();
+ pref.width += i.left + i.right;
+ pref.height += i.top + i.bottom;
+
+ // Return a copy, so that nobody messes with our textRect.
+ return pref.getSize();
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "MenuItem"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "MenuItem";
+ }
+
+ /**
+ * 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()
+ {
+ String prefix = getPropertyPrefix();
+ LookAndFeel.installBorder(menuItem, prefix + ".border");
+ LookAndFeel.installColorsAndFont(menuItem, prefix + ".background",
+ prefix + ".foreground", prefix + ".font");
+ menuItem.setMargin(UIManager.getInsets(prefix + ".margin"));
+ acceleratorFont = UIManager.getFont(prefix + ".acceleratorFont");
+ acceleratorForeground = UIManager.getColor(prefix
+ + ".acceleratorForeground");
+ acceleratorSelectionForeground = UIManager.getColor(prefix
+ + ".acceleratorSelectionForeground");
+ selectionBackground = UIManager.getColor(prefix + ".selectionBackground");
+ selectionForeground = UIManager.getColor(prefix + ".selectionForeground");
+ acceleratorDelimiter = UIManager.getString(prefix + ".acceleratorDelimiter");
+ checkIcon = UIManager.getIcon(prefix + ".checkIcon");
+
+ menuItem.setHorizontalTextPosition(SwingConstants.TRAILING);
+ menuItem.setHorizontalAlignment(SwingConstants.LEADING);
+ }
+
+ /**
+ * This method installs the keyboard actions for this {@link JMenuItem}.
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap focusedWindowMap = SwingUtilities.getUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ if (focusedWindowMap == null)
+ focusedWindowMap = new ComponentInputMapUIResource(menuItem);
+ KeyStroke accelerator = menuItem.getAccelerator();
+ if (accelerator != null)
+ focusedWindowMap.put(accelerator, "doClick");
+ SwingUtilities.replaceUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, focusedWindowMap);
+
+ ActionMap UIActionMap = SwingUtilities.getUIActionMap(menuItem);
+ if (UIActionMap == null)
+ UIActionMap = new ActionMapUIResource();
+ UIActionMap.put("doClick", new ClickAction());
+ SwingUtilities.replaceUIActionMap(menuItem, UIActionMap);
+ }
+
+ /**
+ * 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.addItemListener(itemListener);
+ menuItem.addPropertyChangeListener(propertyChangeListener);
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ propertyChangeListener.propertyChange(new PropertyChangeEvent(menuItem,
+ "font", null,
+ menuItem.getFont()));
+ }
+
+ /**
+ * 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();
+ installKeyboardActions();
+ }
+
+ /**
+ * 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, selectionBackground,
+ 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)
+ {
+ // Menu item is considered to be highlighted when it is selected.
+ // But we don't want to paint the background of JCheckBoxMenuItems
+ ButtonModel mod = menuItem.getModel();
+ Color saved = g.getColor();
+ if (mod.isArmed() || ((menuItem instanceof JMenu) && mod.isSelected()))
+ {
+ g.setColor(bgColor);
+ g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
+ }
+ else if (menuItem.isOpaque())
+ {
+ g.setColor(menuItem.getBackground());
+ g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
+ }
+ g.setColor(saved);
+ }
+
+ /**
+ * 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;
+
+ // Fetch fonts.
+ Font oldFont = g.getFont();
+ Font font = c.getFont();
+ g.setFont(font);
+ FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
+
+ // Create accelerator string.
+ String accelText = getAcceleratorString(m);
+
+ // Layout menu item. The result gets stored in the rectangle fields
+ // of this class.
+ resetRectangles(m);
+
+ layoutMenuItem(m, accelText);
+
+ // Paint the background.
+ paintBackground(g, m, background);
+
+ Color oldColor = g.getColor();
+
+ // Paint the check icon.
+ if (checkIcon != null)
+ {
+ checkIcon.paintIcon(m, g, checkIconRect.x, checkIconRect.y);
+ }
+
+ // Paint the icon.
+ ButtonModel model = m.getModel();
+ if (m.getIcon() != null)
+ {
+ // Determine icon depending on the menu item
+ // state (normal/disabled/pressed).
+ Icon icon;
+ if (! m.isEnabled())
+ {
+ icon = m.getDisabledIcon();
+ }
+ else if (model.isPressed() && model.isArmed())
+ {
+ icon = m.getPressedIcon();
+ if (icon == null)
+ {
+ icon = m.getIcon();
+ }
+ }
+ else
+ {
+ icon = m.getIcon();
+ }
+
+ if (icon != null)
+ {
+ icon.paintIcon(m, g, iconRect.x, iconRect.y);
+ }
+ }
+
+ // Paint the text.
+ String text = m.getText();
+ if (text != null)
+ {
+ // Handle HTML.
+ View html = (View) m.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ html.paint(g, textRect);
+ }
+ else
+ {
+ paintText(g, m, textRect, text);
+ }
+ }
+
+ // Paint accelerator text.
+ if (! accelText.equals(""))
+ {
+ // Align the accelerator text. In getPreferredMenuItemSize() we
+ // store a client property 'maxAccelWidth' in the parent which holds
+ // the maximum accelerator width for the children of this parent.
+ // We use this here to align the accelerators properly.
+ int accelOffset = 0;
+ Container parent = m.getParent();
+ if (parent != null && parent instanceof JComponent)
+ {
+ JComponent p = (JComponent) parent;
+ Integer maxAccelWidth =
+ (Integer) p.getClientProperty("maxAccelWidth");
+ int maxAccelValue = maxAccelWidth == null ? 0
+ : maxAccelWidth.intValue();
+ accelOffset = maxAccelValue - accelRect.width;
+ }
+
+ g.setFont(acceleratorFont);
+ if (! m.isEnabled())
+ {
+ // Paint accelerator disabled.
+ g.setColor(disabledForeground);
+ }
+ else
+ {
+ if (m.isArmed() || (m instanceof JMenu && m.isSelected()))
+ g.setColor(acceleratorSelectionForeground);
+ else
+ g.setColor(acceleratorForeground);
+ }
+ g.drawString(accelText, accelRect.x - accelOffset,
+ accelRect.y + accelFm.getAscent());
+ }
+
+ // Paint arrow.
+ if (arrowIcon != null
+ && ! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ arrowIcon.paintIcon(m, g, arrowIconRect.x, arrowIconRect.y);
+ }
+
+ g.setFont(oldFont);
+ g.setColor(oldColor);
+
+ }
+
+ /**
+ * 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.
+ // But not if it's a JCheckBoxMenuItem
+ ButtonModel mod = menuItem.getModel();
+ if ((menuItem.isSelected() && checkIcon == null)
+ || (mod != null && mod.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(menuItem, g, text,
+ mnemonicIndex,
+ textRect.x,
+ textRect.y
+ + fm.getAscent());
+ else
+ BasicGraphicsUtils.drawString(menuItem, 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()
+ {
+ SwingUtilities.replaceUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, null);
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using.
+ */
+ protected void uninstallListeners()
+ {
+ menuItem.removeMouseListener(mouseInputListener);
+ menuItem.removeMenuDragMouseListener(menuDragMouseListener);
+ menuItem.removeMenuKeyListener(menuKeyListener);
+ menuItem.removeItemListener(itemListener);
+ 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);
+ c.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
+ 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);
+ }
+
+ /**
+ * 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()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * 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)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ int x = e.getX();
+ int y = e.getY();
+ if (x > 0 && x < menuItem.getWidth() && y > 0
+ && y < menuItem.getHeight())
+ {
+ doClick(manager);
+ }
+ else
+ manager.processMouseEvent(e);
+ }
+ }
+
+ /**
+ * This class handles mouse dragged events.
+ */
+ private 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 = e.getMenuSelectionManager();
+ 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 = e.getMenuSelectionManager();
+ 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)
+ {
+ // Nothing to do here yet.
+ }
+
+ /**
+ * Tbis method is invoked when mouse was dragged and released inside the
+ * menu item.
+ *
+ * @param e
+ * The MenuDragMouseEvent
+ */
+ public void menuDragMouseReleased(MenuDragMouseEvent e)
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ int x = e.getX();
+ int y = e.getY();
+ if (x >= 0 && x < menuItem.getWidth() && y >= 0
+ && y < menuItem.getHeight())
+ doClick(manager);
+ else
+ manager.clearSelectedPath();
+ }
+ }
+
+ /**
+ * This class handles key events occuring when menu item is visible on the
+ * screen.
+ */
+ private class MenuKeyHandler implements MenuKeyListener
+ {
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e
+ * A {@link MenuKeyEvent}.
+ */
+ public void menuKeyPressed(MenuKeyEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e
+ * A {@link MenuKeyEvent}.
+ */
+ public void menuKeyReleased(MenuKeyEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * 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)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+
+ /**
+ * Helper class that listens for item changes to the properties of the {@link
+ * JMenuItem}.
+ */
+ private class ItemHandler implements ItemListener
+ {
+ /**
+ * This method is called when one of the menu item changes.
+ *
+ * @param evt A {@link ItemEvent}.
+ */
+ public void itemStateChanged(ItemEvent evt)
+ {
+ boolean state = false;
+ if (menuItem instanceof JCheckBoxMenuItem)
+ {
+ if (evt.getStateChange() == ItemEvent.SELECTED)
+ state = true;
+ ((JCheckBoxMenuItem) menuItem).setState(state);
+ }
+ menuItem.revalidate();
+ menuItem.repaint();
+ }
+ }
+
+ /**
+ * A helper method to create the accelerator string from the menu item's
+ * accelerator property. The returned string is empty if there is
+ * no accelerator defined.
+ *
+ * @param m the menu item
+ *
+ * @return the accelerator string, not null
+ */
+ private String getAcceleratorString(JMenuItem m)
+ {
+ // Create accelerator string.
+ KeyStroke accel = m.getAccelerator();
+ String accelText = "";
+ if (accel != null)
+ {
+ int mods = accel.getModifiers();
+ if (mods > 0)
+ {
+ accelText = KeyEvent.getKeyModifiersText(mods);
+ accelText += acceleratorDelimiter;
+ }
+ int keycode = accel.getKeyCode();
+ if (keycode != 0)
+ accelText += KeyEvent.getKeyText(keycode);
+ else
+ accelText += accel.getKeyChar();
+ }
+ return accelText;
+ }
+
+ /**
+ * Resets the cached layout rectangles. If <code>i</code> is not null, then
+ * the view rectangle is set to the inner area of the component, otherwise
+ * it is set to (0, 0, Short.MAX_VALUE, Short.MAX_VALUE), this is needed
+ * for layouting.
+ *
+ * @param i the component for which to initialize the rectangles
+ */
+ private void resetRectangles(JMenuItem i)
+ {
+ // Reset rectangles.
+ iconRect.setBounds(0, 0, 0, 0);
+ textRect.setBounds(0, 0, 0, 0);
+ accelRect.setBounds(0, 0, 0, 0);
+ checkIconRect.setBounds(0, 0, 0, 0);
+ arrowIconRect.setBounds(0, 0, 0, 0);
+ if (i == null)
+ viewRect.setBounds(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
+ else
+ {
+ Insets insets = i.getInsets();
+ viewRect.setBounds(insets.left, insets.top,
+ i.getWidth() - insets.left - insets.right,
+ i.getHeight() - insets.top - insets.bottom);
+ }
+ }
+
+ /**
+ * A helper method that lays out the menu item. The layout is stored
+ * in the fields of this class.
+ *
+ * @param m the menu item to layout
+ * @param accelText the accelerator text
+ */
+ private void layoutMenuItem(JMenuItem m, String accelText)
+ {
+ // Fetch the fonts.
+ Font font = m.getFont();
+ FontMetrics fm = m.getFontMetrics(font);
+ FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
+
+ String text = m.getText();
+ SwingUtilities.layoutCompoundLabel(m, fm, text, m.getIcon(),
+ m.getVerticalAlignment(),
+ m.getHorizontalAlignment(),
+ m.getVerticalTextPosition(),
+ m.getHorizontalTextPosition(),
+ viewRect, iconRect, textRect,
+ defaultTextIconGap);
+
+ // Initialize accelerator width and height.
+ if (! accelText.equals(""))
+ {
+ accelRect.width = accelFm.stringWidth(accelText);
+ accelRect.height = accelFm.getHeight();
+ }
+
+ // Initialize check and arrow icon width and height.
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ if (checkIcon != null)
+ {
+ checkIconRect.width = checkIcon.getIconWidth();
+ checkIconRect.height = checkIcon.getIconHeight();
+ }
+ if (arrowIcon != null)
+ {
+ arrowIconRect.width = arrowIcon.getIconWidth();
+ arrowIconRect.height = arrowIcon.getIconHeight();
+ }
+ }
+
+ // The union of the icon and text of the menu item is the 'label area'.
+ cachedRect.setBounds(textRect);
+ Rectangle labelRect = SwingUtilities.computeUnion(iconRect.x,
+ iconRect.y,
+ iconRect.width,
+ iconRect.height,
+ cachedRect);
+ textRect.x += defaultTextIconGap;
+ iconRect.x += defaultTextIconGap;
+
+ // Layout accelerator rect.
+ accelRect.x = viewRect.x + viewRect.width - arrowIconRect.width
+ - defaultTextIconGap - accelRect.width;
+ // Layout check and arrow icons only when not in toplevel menu.
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ checkIconRect.x = viewRect.x + defaultTextIconGap;
+ textRect.x += defaultTextIconGap + checkIconRect.width;
+ iconRect.x += defaultTextIconGap + checkIconRect.width;
+ arrowIconRect.x = viewRect.x + viewRect.width - defaultTextIconGap
+ - arrowIconRect.width;
+ }
+
+ // Align the accelerator text and all the icons vertically centered to
+ // the menu text.
+ accelRect.y = labelRect.y + (labelRect.height / 2)
+ - (accelRect.height / 2);
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ arrowIconRect.y = labelRect.y + (labelRect.height / 2)
+ - (arrowIconRect.height / 2);
+ checkIconRect.y = labelRect.y + (labelRect.height / 2)
+ - (checkIconRect.height / 2);
+ }
+ }
+}