jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuUI.java
changeset 2 90ce3da70b43
child 1290 da8902cd496c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuUI.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,630 @@
+/*
+ * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.swing.plaf.basic;
+
+import sun.swing.DefaultLookup;
+import sun.swing.UIAction;
+import java.awt.*;
+import java.awt.event.*;
+import java.beans.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.plaf.*;
+import javax.swing.border.*;
+import java.util.Arrays;
+import java.util.ArrayList;
+
+
+/**
+ * A default L&F implementation of MenuUI.  This implementation
+ * is a "combined" view/controller.
+ *
+ * @author Georges Saab
+ * @author David Karlton
+ * @author Arnaud Weber
+ */
+public class BasicMenuUI extends BasicMenuItemUI
+{
+    protected ChangeListener         changeListener;
+    protected MenuListener           menuListener;
+
+    private int lastMnemonic = 0;
+
+    /** Uses as the parent of the windowInputMap when selected. */
+    private InputMap selectedWindowInputMap;
+
+    /* diagnostic aids -- should be false for production builds. */
+    private static final boolean TRACE =   false; // trace creates and disposes
+    private static final boolean VERBOSE = false; // show reuse hits/misses
+    private static final boolean DEBUG =   false;  // show bad params, misc.
+
+    private static boolean crossMenuMnemonic = true;
+
+    public static ComponentUI createUI(JComponent x) {
+        return new BasicMenuUI();
+    }
+
+    static void loadActionMap(LazyActionMap map) {
+        BasicMenuItemUI.loadActionMap(map);
+        map.put(new Actions(Actions.SELECT, null, true));
+    }
+
+
+    protected void installDefaults() {
+        super.installDefaults();
+        updateDefaultBackgroundColor();
+        ((JMenu)menuItem).setDelay(200);
+        crossMenuMnemonic = UIManager.getBoolean("Menu.crossMenuMnemonic");
+    }
+
+    protected String getPropertyPrefix() {
+        return "Menu";
+    }
+
+    protected void installListeners() {
+        super.installListeners();
+
+        if (changeListener == null)
+            changeListener = createChangeListener(menuItem);
+
+        if (changeListener != null)
+            menuItem.addChangeListener(changeListener);
+
+        if (menuListener == null)
+            menuListener = createMenuListener(menuItem);
+
+        if (menuListener != null)
+            ((JMenu)menuItem).addMenuListener(menuListener);
+    }
+
+    protected void installKeyboardActions() {
+        super.installKeyboardActions();
+        updateMnemonicBinding();
+    }
+
+    void installLazyActionMap() {
+        LazyActionMap.installLazyActionMap(menuItem, BasicMenuUI.class,
+                                           getPropertyPrefix() + ".actionMap");
+    }
+
+    void updateMnemonicBinding() {
+        int mnemonic = menuItem.getModel().getMnemonic();
+        int[] shortcutKeys = (int[])DefaultLookup.get(menuItem, this,
+                                                   "Menu.shortcutKeys");
+        if (shortcutKeys == null) {
+            shortcutKeys = new int[] {KeyEvent.ALT_MASK};
+        }
+        if (mnemonic == lastMnemonic) {
+            return;
+        }
+        InputMap windowInputMap = SwingUtilities.getUIInputMap(
+                       menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW);
+        if (lastMnemonic != 0 && windowInputMap != null) {
+            for (int i=0; i<shortcutKeys.length; i++) {
+                windowInputMap.remove(KeyStroke.getKeyStroke
+                                      (lastMnemonic, shortcutKeys[i], false));
+            }
+        }
+        if (mnemonic != 0) {
+            if (windowInputMap == null) {
+                windowInputMap = createInputMap(JComponent.
+                                              WHEN_IN_FOCUSED_WINDOW);
+                SwingUtilities.replaceUIInputMap(menuItem, JComponent.
+                                       WHEN_IN_FOCUSED_WINDOW, windowInputMap);
+            }
+            for (int i=0; i<shortcutKeys.length; i++) {
+                windowInputMap.put(KeyStroke.getKeyStroke(mnemonic,
+                                         shortcutKeys[i], false),
+                                   "selectMenu");
+            }
+        }
+        lastMnemonic = mnemonic;
+    }
+
+    protected void uninstallKeyboardActions() {
+        super.uninstallKeyboardActions();
+        lastMnemonic = 0;
+    }
+
+    protected MouseInputListener createMouseInputListener(JComponent c) {
+        return getHandler();
+    }
+
+    protected MenuListener createMenuListener(JComponent c) {
+        return null;
+    }
+
+    protected ChangeListener createChangeListener(JComponent c) {
+        return null;
+    }
+
+    protected PropertyChangeListener createPropertyChangeListener(JComponent c) {
+        return getHandler();
+    }
+
+    BasicMenuItemUI.Handler getHandler() {
+        if (handler == null) {
+            handler = new Handler();
+        }
+        return handler;
+    }
+
+    protected void uninstallDefaults() {
+        menuItem.setArmed(false);
+        menuItem.setSelected(false);
+        menuItem.resetKeyboardActions();
+        super.uninstallDefaults();
+    }
+
+    protected void uninstallListeners() {
+        super.uninstallListeners();
+
+        if (changeListener != null)
+            menuItem.removeChangeListener(changeListener);
+
+        if (menuListener != null)
+            ((JMenu)menuItem).removeMenuListener(menuListener);
+
+        changeListener = null;
+        menuListener = null;
+        handler = null;
+    }
+
+    protected MenuDragMouseListener createMenuDragMouseListener(JComponent c) {
+        return getHandler();
+    }
+
+    protected MenuKeyListener createMenuKeyListener(JComponent c) {
+        return (MenuKeyListener)getHandler();
+    }
+
+    public Dimension getMaximumSize(JComponent c) {
+        if (((JMenu)menuItem).isTopLevelMenu() == true) {
+            Dimension d = c.getPreferredSize();
+            return new Dimension(d.width, Short.MAX_VALUE);
+        }
+        return null;
+    }
+
+    protected void setupPostTimer(JMenu menu) {
+        Timer timer = new Timer(menu.getDelay(), new Actions(
+                                    Actions.SELECT, menu,false));
+        timer.setRepeats(false);
+        timer.start();
+    }
+
+    private static void appendPath(MenuElement[] path, MenuElement elem) {
+        MenuElement newPath[] = new MenuElement[path.length+1];
+        System.arraycopy(path, 0, newPath, 0, path.length);
+        newPath[path.length] = elem;
+        MenuSelectionManager.defaultManager().setSelectedPath(newPath);
+    }
+
+    private static class Actions extends UIAction {
+        private static final String SELECT = "selectMenu";
+
+        // NOTE: This will be null if the action is registered in the
+        // ActionMap. For the timer use it will be non-null.
+        private JMenu menu;
+        private boolean force=false;
+
+        Actions(String key, JMenu menu, boolean shouldForce) {
+            super(key);
+            this.menu = menu;
+            this.force = shouldForce;
+        }
+
+        private JMenu getMenu(ActionEvent e) {
+            if (e.getSource() instanceof JMenu) {
+                return (JMenu)e.getSource();
+            }
+            return menu;
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            JMenu menu = getMenu(e);
+            if (!crossMenuMnemonic) {
+                JPopupMenu pm = BasicPopupMenuUI.getLastPopup();
+                if (pm != null && pm != menu.getParent()) {
+                    return;
+                }
+            }
+
+            final MenuSelectionManager defaultManager = MenuSelectionManager.defaultManager();
+            if(force) {
+                Container cnt = menu.getParent();
+                if(cnt != null && cnt instanceof JMenuBar) {
+                    MenuElement me[];
+                    MenuElement subElements[];
+
+                    subElements = menu.getPopupMenu().getSubElements();
+                    if(subElements.length > 0) {
+                        me = new MenuElement[4];
+                        me[0] = (MenuElement) cnt;
+                        me[1] = (MenuElement) menu;
+                        me[2] = (MenuElement) menu.getPopupMenu();
+                        me[3] = subElements[0];
+                    } else {
+                        me = new MenuElement[3];
+                        me[0] = (MenuElement)cnt;
+                        me[1] = menu;
+                        me[2] = (MenuElement) menu.getPopupMenu();
+                    }
+                    defaultManager.setSelectedPath(me);
+                }
+            } else {
+                MenuElement path[] = defaultManager.getSelectedPath();
+                if(path.length > 0 && path[path.length-1] == menu) {
+                    appendPath(path, menu.getPopupMenu());
+                }
+            }
+        }
+
+        public boolean isEnabled(Object c) {
+            if (c instanceof JMenu) {
+                return ((JMenu)c).isEnabled();
+            }
+            return true;
+        }
+    }
+
+    /*
+     * Set the background color depending on whether this is a toplevel menu
+     * in a menubar or a submenu of another menu.
+     */
+    private void updateDefaultBackgroundColor() {
+        if (!UIManager.getBoolean("Menu.useMenuBarBackgroundForTopLevel")) {
+           return;
+        }
+        JMenu menu = (JMenu)menuItem;
+        if (menu.getBackground() instanceof UIResource) {
+            if (menu.isTopLevelMenu()) {
+                menu.setBackground(UIManager.getColor("MenuBar.background"));
+            } else {
+                menu.setBackground(UIManager.getColor(getPropertyPrefix() + ".background"));
+            }
+        }
+    }
+
+    /**
+     * Instantiated and used by a menu item to handle the current menu selection
+     * from mouse events. A MouseInputHandler processes and forwards all mouse events
+     * to a shared instance of the MenuSelectionManager.
+     * <p>
+     * This class is protected so that it can be subclassed by other look and
+     * feels to implement their own mouse handling behavior. All overridden
+     * methods should call the parent methods so that the menu selection
+     * is correct.
+     *
+     * @see javax.swing.MenuSelectionManager
+     * @since 1.4
+     */
+    protected class MouseInputHandler implements MouseInputListener {
+        // NOTE: This class exists only for backward compatability. All
+        // its functionality has been moved into Handler. If you need to add
+        // new functionality add it to the Handler, but make sure this
+        // class calls into the Handler.
+
+        public void mouseClicked(MouseEvent e) {
+            getHandler().mouseClicked(e);
+        }
+
+        /**
+         * Invoked when the mouse has been clicked on the menu. This
+         * method clears or sets the selection path of the
+         * MenuSelectionManager.
+         *
+         * @param e the mouse event
+         */
+        public void mousePressed(MouseEvent e) {
+            getHandler().mousePressed(e);
+        }
+
+        /**
+         * Invoked when the mouse has been released on the menu. Delegates the
+         * mouse event to the MenuSelectionManager.
+         *
+         * @param e the mouse event
+         */
+        public void mouseReleased(MouseEvent e) {
+            getHandler().mouseReleased(e);
+        }
+
+        /**
+         * Invoked when the cursor enters the menu. This method sets the selected
+         * path for the MenuSelectionManager and handles the case
+         * in which a menu item is used to pop up an additional menu, as in a
+         * hierarchical menu system.
+         *
+         * @param e the mouse event; not used
+         */
+        public void mouseEntered(MouseEvent e) {
+            getHandler().mouseEntered(e);
+        }
+        public void mouseExited(MouseEvent e) {
+            getHandler().mouseExited(e);
+        }
+
+        /**
+         * Invoked when a mouse button is pressed on the menu and then dragged.
+         * Delegates the mouse event to the MenuSelectionManager.
+         *
+         * @param e the mouse event
+         * @see java.awt.event.MouseMotionListener#mouseDragged
+         */
+        public void mouseDragged(MouseEvent e) {
+            getHandler().mouseDragged(e);
+        }
+
+        public void mouseMoved(MouseEvent e) {
+            getHandler().mouseMoved(e);
+        }
+    }
+
+    /**
+     * As of Java 2 platform 1.4, this previously undocumented class
+     * is now obsolete. KeyBindings are now managed by the popup menu.
+     */
+    public class ChangeHandler implements ChangeListener {
+        public JMenu    menu;
+        public BasicMenuUI ui;
+        public boolean  isSelected = false;
+        public Component wasFocused;
+
+        public ChangeHandler(JMenu m, BasicMenuUI ui) {
+            menu = m;
+            this.ui = ui;
+        }
+
+        public void stateChanged(ChangeEvent e) { }
+    }
+
+    private class Handler extends BasicMenuItemUI.Handler implements
+            MenuKeyListener {
+        //
+        // PropertyChangeListener
+        //
+        public void propertyChange(PropertyChangeEvent e) {
+            if (e.getPropertyName() == AbstractButton.
+                             MNEMONIC_CHANGED_PROPERTY) {
+                updateMnemonicBinding();
+            }
+            else {
+                if (e.getPropertyName().equals("ancestor")) {
+                    updateDefaultBackgroundColor();
+                }
+                super.propertyChange(e);
+            }
+        }
+
+        //
+        // MouseInputListener
+        //
+        public void mouseClicked(MouseEvent e) {
+        }
+
+        /**
+         * Invoked when the mouse has been clicked on the menu. This
+         * method clears or sets the selection path of the
+         * MenuSelectionManager.
+         *
+         * @param e the mouse event
+         */
+        public void mousePressed(MouseEvent e) {
+            JMenu menu = (JMenu)menuItem;
+            if (!menu.isEnabled())
+                return;
+
+            MenuSelectionManager manager =
+                MenuSelectionManager.defaultManager();
+            if(menu.isTopLevelMenu()) {
+                if(menu.isSelected() && menu.getPopupMenu().isShowing()) {
+                    manager.clearSelectedPath();
+                } else {
+                    Container cnt = menu.getParent();
+                    if(cnt != null && cnt instanceof JMenuBar) {
+                        MenuElement me[] = new MenuElement[2];
+                        me[0]=(MenuElement)cnt;
+                        me[1]=menu;
+                        manager.setSelectedPath(me);
+                    }
+                }
+            }
+
+            MenuElement selectedPath[] = manager.getSelectedPath();
+            if (selectedPath.length > 0 &&
+                selectedPath[selectedPath.length-1] != menu.getPopupMenu()) {
+
+                if(menu.isTopLevelMenu() ||
+                   menu.getDelay() == 0) {
+                    appendPath(selectedPath, menu.getPopupMenu());
+                } else {
+                    setupPostTimer(menu);
+                }
+            }
+        }
+
+        /**
+         * Invoked when the mouse has been released on the menu. Delegates the
+         * mouse event to the MenuSelectionManager.
+         *
+         * @param e the mouse event
+         */
+        public void mouseReleased(MouseEvent e) {
+            JMenu menu = (JMenu)menuItem;
+            if (!menu.isEnabled())
+                return;
+            MenuSelectionManager manager =
+                MenuSelectionManager.defaultManager();
+            manager.processMouseEvent(e);
+            if (!e.isConsumed())
+                manager.clearSelectedPath();
+        }
+
+        /**
+         * Invoked when the cursor enters the menu. This method sets the selected
+         * path for the MenuSelectionManager and handles the case
+         * in which a menu item is used to pop up an additional menu, as in a
+         * hierarchical menu system.
+         *
+         * @param e the mouse event; not used
+         */
+        public void mouseEntered(MouseEvent e) {
+            JMenu menu = (JMenu)menuItem;
+            // only disable the menu highlighting if it's disabled and the property isn't
+            // true. This allows disabled rollovers to work in WinL&F
+            if (!menu.isEnabled() && !UIManager.getBoolean("MenuItem.disabledAreNavigable")) {
+                return;
+            }
+
+            MenuSelectionManager manager =
+                MenuSelectionManager.defaultManager();
+            MenuElement selectedPath[] = manager.getSelectedPath();
+            if (!menu.isTopLevelMenu()) {
+                if(!(selectedPath.length > 0 &&
+                     selectedPath[selectedPath.length-1] ==
+                     menu.getPopupMenu())) {
+                    if(menu.getDelay() == 0) {
+                        appendPath(getPath(), menu.getPopupMenu());
+                    } else {
+                        manager.setSelectedPath(getPath());
+                        setupPostTimer(menu);
+                    }
+                }
+            } else {
+                if(selectedPath.length > 0 &&
+                   selectedPath[0] == menu.getParent()) {
+                    MenuElement newPath[] = new MenuElement[3];
+                    // A top level menu's parent is by definition
+                    // a JMenuBar
+                    newPath[0] = (MenuElement)menu.getParent();
+                    newPath[1] = menu;
+                    if (BasicPopupMenuUI.getLastPopup() != null) {
+                        newPath[2] = menu.getPopupMenu();
+                    }
+                    manager.setSelectedPath(newPath);
+                }
+            }
+        }
+        public void mouseExited(MouseEvent e) {
+        }
+
+        /**
+         * Invoked when a mouse button is pressed on the menu and then dragged.
+         * Delegates the mouse event to the MenuSelectionManager.
+         *
+         * @param e the mouse event
+         * @see java.awt.event.MouseMotionListener#mouseDragged
+         */
+        public void mouseDragged(MouseEvent e) {
+            JMenu menu = (JMenu)menuItem;
+            if (!menu.isEnabled())
+                return;
+            MenuSelectionManager.defaultManager().processMouseEvent(e);
+        }
+        public void mouseMoved(MouseEvent e) {
+        }
+
+
+        //
+        // MenuDragHandler
+        //
+        public void menuDragMouseEntered(MenuDragMouseEvent e) {}
+        public void menuDragMouseDragged(MenuDragMouseEvent e) {
+            if (menuItem.isEnabled() == false)
+                return;
+
+            MenuSelectionManager manager = e.getMenuSelectionManager();
+            MenuElement path[] = e.getPath();
+
+            Point p = e.getPoint();
+            if(p.x >= 0 && p.x < menuItem.getWidth() &&
+               p.y >= 0 && p.y < menuItem.getHeight()) {
+                JMenu menu = (JMenu)menuItem;
+                MenuElement selectedPath[] = manager.getSelectedPath();
+                if(!(selectedPath.length > 0 &&
+                     selectedPath[selectedPath.length-1] ==
+                     menu.getPopupMenu())) {
+                    if(menu.isTopLevelMenu() ||
+                       menu.getDelay() == 0  ||
+                       e.getID() == MouseEvent.MOUSE_DRAGGED) {
+                        appendPath(path, menu.getPopupMenu());
+                    } else {
+                        manager.setSelectedPath(path);
+                        setupPostTimer(menu);
+                    }
+                }
+            } else if(e.getID() == MouseEvent.MOUSE_RELEASED) {
+                Component comp = manager.componentForPoint(e.getComponent(), e.getPoint());
+                if (comp == null)
+                    manager.clearSelectedPath();
+            }
+
+        }
+        public void menuDragMouseExited(MenuDragMouseEvent e) {}
+        public void menuDragMouseReleased(MenuDragMouseEvent e) {}
+
+
+        //
+        // MenuKeyListener
+        //
+        /**
+         * Open the Menu
+         */
+        public void menuKeyTyped(MenuKeyEvent e) {
+            if (!crossMenuMnemonic && BasicPopupMenuUI.getLastPopup() != null) {
+                // when crossMenuMnemonic is not set, we don't open a toplevel
+                // menu if another toplevel menu is already open
+                    return;
+                }
+
+            char key = Character.toLowerCase((char)menuItem.getMnemonic());
+            MenuElement path[] = e.getPath();
+            MenuSelectionManager manager = e.getMenuSelectionManager();
+            if (key == Character.toLowerCase(e.getKeyChar())) {
+                JPopupMenu popupMenu = ((JMenu)menuItem).getPopupMenu();
+                ArrayList newList = new ArrayList(Arrays.asList(path));
+                newList.add(popupMenu);
+                MenuElement subs[] = popupMenu.getSubElements();
+                MenuElement sub =
+                        BasicPopupMenuUI.findEnabledChild(subs, -1, true);
+                if(sub != null) {
+                    newList.add(sub);
+                }
+                MenuElement newPath[] = new MenuElement[0];;
+                newPath = (MenuElement[]) newList.toArray(newPath);
+                manager.setSelectedPath(newPath);
+                e.consume();
+            } else if (((JMenu)menuItem).isTopLevelMenu()
+                    && BasicPopupMenuUI.getLastPopup() == null) {
+                manager.clearSelectedPath();
+            }
+        }
+
+        public void menuKeyPressed(MenuKeyEvent e) {}
+        public void menuKeyReleased(MenuKeyEvent e) {}
+    }
+}