FileDocCategorySizeDatePackage
BasicPopupMenuUI.javaAPI DocJava SE 5 API40844Fri Aug 26 14:58:04 BST 2005javax.swing.plaf.basic

BasicPopupMenuUI.java

/*
 * @(#)BasicPopupMenuUI.java	1.122 05/06/06
 *
 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.swing.plaf.basic;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.border.*;

import java.applet.Applet;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.KeyboardFocusManager;
import java.awt.Window;
import java.awt.event.*;
import java.awt.AWTEvent;
import java.awt.Toolkit;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;

import java.util.*;

import sun.swing.DefaultLookup;
import sun.swing.UIAction;

/**
 * A Windows L&F implementation of PopupMenuUI.  This implementation 
 * is a "combined" view/controller.
 *
 * @version 1.122 06/06/05
 * @author Georges Saab
 * @author David Karlton
 * @author Arnaud Weber
 */
public class BasicPopupMenuUI extends PopupMenuUI {
    protected JPopupMenu popupMenu = null;
    private transient PopupMenuListener popupMenuListener = null;
    private MenuKeyListener menuKeyListener = null;
    static boolean menuKeyboardHelperInstalled = false;
    static MenuKeyboardHelper menuKeyboardHelper = null;

    private static boolean checkedUnpostPopup;
    private static boolean unpostPopup;

    public static ComponentUI createUI(JComponent x) {
	return new BasicPopupMenuUI();
    }

    public BasicPopupMenuUI() {
        BasicLookAndFeel.hasPopups = true;
        LookAndFeel laf = UIManager.getLookAndFeel();
        if (laf instanceof BasicLookAndFeel) {
            ((BasicLookAndFeel)laf).createdPopup();
        }
    }

    public void installUI(JComponent c) {
	popupMenu = (JPopupMenu) c;

	installDefaults();
        installListeners();
        installKeyboardActions();
    }

    public void installDefaults() {
	if (popupMenu.getLayout() == null ||
	    popupMenu.getLayout() instanceof UIResource)
	    popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS));

	LookAndFeel.installProperty(popupMenu, "opaque", Boolean.TRUE);
	LookAndFeel.installBorder(popupMenu, "PopupMenu.border");
	LookAndFeel.installColorsAndFont(popupMenu,
					 "PopupMenu.background",
					 "PopupMenu.foreground",
					 "PopupMenu.font");
    }
    
    protected void installListeners() {
        if (popupMenuListener == null) {
	    popupMenuListener = new BasicPopupMenuListener();
	}
 	popupMenu.addPopupMenuListener(popupMenuListener);

        if (menuKeyListener == null) {
            menuKeyListener = new BasicMenuKeyListener();
        }
        popupMenu.addMenuKeyListener(menuKeyListener);

	if (mouseGrabber == null) {
	    mouseGrabber = new MouseGrabber();
	}

        if (!menuKeyboardHelperInstalled) {
            if (menuKeyboardHelper == null) {
                menuKeyboardHelper = new MenuKeyboardHelper();
            }
            MenuSelectionManager msm = MenuSelectionManager.defaultManager();
            msm.addChangeListener(menuKeyboardHelper);
            menuKeyboardHelperInstalled = true;
        }
    }

    protected void installKeyboardActions() {
    }

    static InputMap getInputMap(JPopupMenu popup, JComponent c) {
        InputMap windowInputMap = null;
	Object[] bindings = (Object[])UIManager.get("PopupMenu.selectedWindowInputMapBindings");
	if (bindings != null) {
	    windowInputMap = LookAndFeel.makeComponentInputMap(c, bindings);
	    if (!popup.getComponentOrientation().isLeftToRight()) {
		Object[] km = (Object[])UIManager.get("PopupMenu.selectedWindowInputMapBindings.RightToLeft");
		if (km != null) {
		    InputMap rightToLeftInputMap = LookAndFeel.makeComponentInputMap(c, km);
		    rightToLeftInputMap.setParent(windowInputMap);
		    windowInputMap = rightToLeftInputMap;
		}
	    }
        }
        return windowInputMap;
    }

    static ActionMap getActionMap() {
        return LazyActionMap.getActionMap(BasicPopupMenuUI.class,
                                          "PopupMenu.actionMap");
    }
  
    static void loadActionMap(LazyActionMap map) {
	map.put(new Actions(Actions.CANCEL));
        map.put(new Actions(Actions.SELECT_NEXT));
        map.put(new Actions(Actions.SELECT_PREVIOUS));
	map.put(new Actions(Actions.SELECT_PARENT));
	map.put(new Actions(Actions.SELECT_CHILD));
	map.put(new Actions(Actions.RETURN));
        BasicLookAndFeel.installAudioActionMap(map);
    }

    public void uninstallUI(JComponent c) {
        uninstallDefaults();
        uninstallListeners();
        uninstallKeyboardActions();
	
	popupMenu = null;
    }
    
    protected void uninstallDefaults() {
	LookAndFeel.uninstallBorder(popupMenu);
    }

    protected void uninstallListeners() {
        if (popupMenuListener != null) {
            popupMenu.removePopupMenuListener(popupMenuListener);
	}
        if (menuKeyListener != null) {
            popupMenu.removeMenuKeyListener(menuKeyListener);
        }
        if(mouseGrabber != null) {
            MenuSelectionManager msm = MenuSelectionManager.defaultManager();
            msm.removeChangeListener(mouseGrabber);
            mouseGrabber.ungrabWindow();
            mouseGrabber = null;
        }
    }

    protected void uninstallKeyboardActions() {
	SwingUtilities.replaceUIActionMap(popupMenu, null);
	SwingUtilities.replaceUIInputMap(popupMenu, 
				  JComponent.WHEN_IN_FOCUSED_WINDOW, null);
    }

    static MenuElement getFirstPopup() {
	MenuSelectionManager msm = MenuSelectionManager.defaultManager();
	MenuElement[] p = msm.getSelectedPath();
	MenuElement me = null;	    
	
	for(int i = 0 ; me == null && i < p.length ; i++) {
	    if (p[i] instanceof JPopupMenu)
		me = p[i];
	}
	
	return me;
    }

    private static boolean doUnpostPopupOnDeactivation() {
        if (!checkedUnpostPopup) {
            Boolean b = java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction <Boolean> () {
                    public Boolean run() {
                        String pKey =
                            "sun.swing.unpostPopupsOnWindowDeactivation";
                        String value = System.getProperty(pKey, "true");
                        return Boolean.valueOf(value);
                    }
                }
            );
            unpostPopup = b.booleanValue();
            checkedUnpostPopup = true;
        }
        return unpostPopup;
    }

    static JPopupMenu getLastPopup() {
	MenuSelectionManager msm = MenuSelectionManager.defaultManager();
	MenuElement[] p = msm.getSelectedPath();
	JPopupMenu popup = null;	    
	
	for(int i = p.length - 1; popup == null && i >= 0; i--) {
	    if (p[i] instanceof JPopupMenu)
		popup = (JPopupMenu)p[i];
	}
	return popup;
    }

    static List getPopups() {
	MenuSelectionManager msm = MenuSelectionManager.defaultManager();
	MenuElement[] p = msm.getSelectedPath();
	
	List list = new ArrayList(p.length); 
	for(int i = 0; i < p.length; i++) {
	    if (p[i] instanceof JPopupMenu) {
		list.add((JPopupMenu)p[i]);
	    }
	}
	return list;
    }

    public boolean isPopupTrigger(MouseEvent e) {
	return ((e.getID()==MouseEvent.MOUSE_RELEASED) 
		&& ((e.getModifiers() & MouseEvent.BUTTON3_MASK)!=0));
    }	    

    private static boolean checkInvokerEqual(MenuElement present, MenuElement last) {
        Component invokerPresent = present.getComponent();
        Component invokerLast = last.getComponent();

        if (invokerPresent instanceof JPopupMenu) {
            invokerPresent = ((JPopupMenu)invokerPresent).getInvoker();
    }
        if (invokerLast instanceof JPopupMenu) {
            invokerLast = ((JPopupMenu)invokerLast).getInvoker();
        }
        return (invokerPresent == invokerLast);
    }


    /**
     * This Listener fires the Action that provides the correct auditory
     * feedback.
     *
     * @since 1.4
     */
    private class BasicPopupMenuListener implements PopupMenuListener {
        public void popupMenuCanceled(PopupMenuEvent e) {
	}

        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
	}

        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
            BasicLookAndFeel.playSound((JPopupMenu)e.getSource(),
                                       "PopupMenu.popupSound");
	}
    }

    /**
     * Handles mnemonic for children JMenuItems.
     * @since 1.5
     */
    private class BasicMenuKeyListener implements MenuKeyListener {
        MenuElement menuToOpen = null;

	public void menuKeyTyped(MenuKeyEvent e) {
            if (menuToOpen != null) {
                // we have a submenu to open
                JPopupMenu subpopup = ((JMenu)menuToOpen).getPopupMenu();
                MenuElement subitem = findEnabledChild(
                        subpopup.getSubElements(), -1, true);

                ArrayList lst = new ArrayList(Arrays.asList(e.getPath()));
                lst.add(menuToOpen);
                lst.add(subpopup);
                if (subitem != null) {
                    lst.add(subitem);
                }
                MenuElement newPath[] = new MenuElement[0];;
                newPath = (MenuElement[])lst.toArray(newPath);
                MenuSelectionManager.defaultManager().setSelectedPath(newPath);
                e.consume();
            }
            menuToOpen = null;
        }

	public void menuKeyPressed(MenuKeyEvent e) {
	    // Handle the case for Escape or Enter...
	    if (!Character.isLetterOrDigit(e.getKeyChar())) {
		return;
            }

            int keyCode = e.getKeyCode();
            MenuSelectionManager manager = e.getMenuSelectionManager();
            MenuElement path[] = e.getPath();
            MenuElement items[] = popupMenu.getSubElements();
            int currentIndex = -1;
            int matches = 0;
            int firstMatch = -1;
            int indexes[] = null;

            for (int j = 0; j < items.length; j++) {
                if (! (items[j] instanceof JMenuItem)) {
                    continue;
                }
                JMenuItem item = (JMenuItem)items[j];
                if (item.isEnabled() &&
                    item.isVisible() && keyCode == item.getMnemonic()) {
                    if (matches == 0) {
                        firstMatch = j;
                        matches++;
                    } else {
                        if (indexes == null) {
                            indexes = new int[items.length];
                            indexes[0] = firstMatch;
                        }
                        indexes[matches++] = j;
                    }
                }
                if (item.isArmed()) {
                    currentIndex = matches - 1;
                }
            }

            if (matches == 0) {
                ; // no op
            } else if (matches == 1) {
                // Invoke the menu action
                JMenuItem item = (JMenuItem)items[firstMatch];
                if (item instanceof JMenu) {
                    // submenus are handled in menuKeyTyped
                    menuToOpen = item;
                } else if (item.isEnabled()) {
                    // we have a menu item
                    manager.clearSelectedPath();
                    item.doClick();
                }
                e.consume();
            } else {
                // Select the menu item with the matching mnemonic. If
                // the same mnemonic has been invoked then select the next
                // menu item in the cycle.
                MenuElement newItem = null;

                newItem = items[indexes[(currentIndex + 1) % matches]];

                MenuElement newPath[] = new MenuElement[path.length+1];
                System.arraycopy(path, 0, newPath, 0, path.length);
                newPath[path.length] = newItem;
                manager.setSelectedPath(newPath);
                e.consume();
            }
            return;
	}

	public void menuKeyReleased(MenuKeyEvent e) {
        }
    }

    private static class Actions extends UIAction {
        // Types of actions
        private static final String CANCEL = "cancel";
        private static final String SELECT_NEXT = "selectNext";
        private static final String SELECT_PREVIOUS = "selectPrevious";
        private static final String SELECT_PARENT = "selectParent";
        private static final String SELECT_CHILD = "selectChild";
        private static final String RETURN = "return";

        // Used for next/previous actions
        private static final boolean FORWARD = true;
        private static final boolean BACKWARD = false;

        // Used for parent/child actions
        private static final boolean PARENT = false;
        private static final boolean CHILD = true;


        Actions(String key) {
            super(key);
        }

	public void actionPerformed(ActionEvent e) {
            String key = getName();
            if (key == CANCEL) {
                cancel();
            }
            else if (key == SELECT_NEXT) {
                selectItem(FORWARD);
            }
            else if (key == SELECT_PREVIOUS) {
                selectItem(BACKWARD);
            }
            else if (key == SELECT_PARENT) {
                selectParentChild(PARENT);
            }
            else if (key == SELECT_CHILD) {
                selectParentChild(CHILD);
            }
            else if (key == RETURN) {
                doReturn();
            }
        }

	private void doReturn() {
            KeyboardFocusManager fmgr =
                KeyboardFocusManager.getCurrentKeyboardFocusManager();
            Component focusOwner = fmgr.getFocusOwner();
            if(focusOwner != null && !(focusOwner instanceof JRootPane)) {
                return;
            }

            MenuSelectionManager msm = MenuSelectionManager.defaultManager();
	    MenuElement path[] = msm.getSelectedPath();
	    MenuElement lastElement;
	    if(path.length > 0) {
		lastElement = path[path.length-1];
		if(lastElement instanceof JMenu) {
		    MenuElement newPath[] = new MenuElement[path.length+1];
		    System.arraycopy(path,0,newPath,0,path.length);
		    newPath[path.length] = ((JMenu)lastElement).getPopupMenu();
		    msm.setSelectedPath(newPath);
		} else if(lastElement instanceof JMenuItem) {
                    JMenuItem mi = (JMenuItem)lastElement;

                    if (mi.getUI() instanceof BasicMenuItemUI) {
                        ((BasicMenuItemUI)mi.getUI()).doClick(msm);
                    }
                    else {
                        msm.clearSelectedPath();
                        mi.doClick(0);
                    }
		}
	    }
	}
	private void selectParentChild(boolean direction) {
            MenuSelectionManager msm = MenuSelectionManager.defaultManager();
            MenuElement path[] = msm.getSelectedPath();
            int len = path.length;

            if (direction == PARENT) {
                // selecting parent
                int popupIndex = len-1;

                if (len > 2 &&
                    // check if we have an open submenu. A submenu item may or
                    // may not be selected, so submenu popup can be either the
                    // last or next to the last item.
                    (path[popupIndex] instanceof JPopupMenu ||
                     path[--popupIndex] instanceof JPopupMenu) &&
                    !((JMenu)path[popupIndex-1]).isTopLevelMenu()) {

                    // we have a submenu, just close it
                    MenuElement newPath[] = new MenuElement[popupIndex];
                    System.arraycopy(path, 0, newPath, 0, popupIndex);
                    msm.setSelectedPath(newPath);
                    return;
                }
            } else {
                // selecting child
                if (len > 0 && path[len-1] instanceof JMenu &&
                    !((JMenu)path[len-1]).isTopLevelMenu()) {

                    // we have a submenu, open it
                    JMenu menu = (JMenu)path[len-1];
                    JPopupMenu popup = menu.getPopupMenu();
                    MenuElement[] subs = popup.getSubElements();
                    MenuElement item = findEnabledChild(subs, -1, true);
                    MenuElement[] newPath;

                    if (item == null) {
                        newPath = new MenuElement[len+1];
                    } else {
                        newPath = new MenuElement[len+2];
                        newPath[len+1] = item;
                    }
                    System.arraycopy(path, 0, newPath, 0, len);
                    newPath[len] = popup;
                    msm.setSelectedPath(newPath);
                    return;
                }
            }

            // check if we have a toplevel menu selected.
            // If this is the case, we select another toplevel menu
	    if (len > 1 && path[0] instanceof JMenuBar) {
                MenuElement currentMenu = path[1];
		MenuElement nextMenu = findEnabledChild(
                    path[0].getSubElements(), currentMenu, direction);

		if (nextMenu != null && nextMenu != currentMenu) {
		    MenuElement newSelection[];
		    if (len == 2) {
                        // menu is selected but its popup not shown
			newSelection = new MenuElement[2];
			newSelection[0] = path[0];
			newSelection[1] = nextMenu;
		    } else {
                        // menu is selected and its popup is shown
			newSelection = new MenuElement[3];
			newSelection[0] = path[0];
			newSelection[1] = nextMenu;
			newSelection[2] = ((JMenu)nextMenu).getPopupMenu();
                    }
		    msm.setSelectedPath(newSelection);
		}
	    }
	}

        private void selectItem(boolean direction) {
            MenuSelectionManager msm = MenuSelectionManager.defaultManager();
            MenuElement path[] = msm.getSelectedPath();
            if (path.length < 2) {
                return;
            }
            int len = path.length;

            if (path[0] instanceof JMenuBar &&
                path[1] instanceof JMenu && len == 2) {

                // a toplevel menu is selected, but its popup not shown.
                // Show the popup and select the first item
                JPopupMenu popup = ((JMenu)path[1]).getPopupMenu();
                MenuElement next =
                    findEnabledChild(popup.getSubElements(), -1, FORWARD);
                MenuElement[] newPath;

                if (next != null) {
                    // an enabled item found -- include it in newPath
                    newPath = new MenuElement[4];
                    newPath[3] = next;
                } else {
                    // menu has no enabled items -- still must show the popup
                    newPath = new MenuElement[3];
                }
                System.arraycopy(path, 0, newPath, 0, 2);
                newPath[2] = popup;
                msm.setSelectedPath(newPath);

            } else if (path[len-1] instanceof JPopupMenu &&
                       path[len-2] instanceof JMenu) {

                // a menu (not necessarily toplevel) is open and its popup
                // shown. Select the appropriate menu item
                JMenu menu = (JMenu)path[len-2];
                JPopupMenu popup = menu.getPopupMenu();
                MenuElement next =
                    findEnabledChild(popup.getSubElements(), -1, direction);

                if (next != null) {
                    MenuElement[] newPath = new MenuElement[len+1];
                    System.arraycopy(path, 0, newPath, 0, len);
                    newPath[len] = next;
                    msm.setSelectedPath(newPath);
                } else {
                    // all items in the popup are disabled.
                    // We're going to find the parent popup menu and select
                    // its next item. If there's no parent popup menu (i.e.
                    // current menu is toplevel), do nothing
                    if (len > 2 && path[len-3] instanceof JPopupMenu) {
                        popup = ((JPopupMenu)path[len-3]);
                        next = findEnabledChild(popup.getSubElements(),
                                                menu, direction);

                        if (next != null && next != menu) {
                            MenuElement[] newPath = new MenuElement[len-1];
                            System.arraycopy(path, 0, newPath, 0, len-2);
                            newPath[len-2] = next;
                            msm.setSelectedPath(newPath);
                        }
                    }
                }

            } else {
                // just select the next item, no path expansion needed
                MenuElement subs[] = path[len-2].getSubElements();
                MenuElement nextChild =
                    findEnabledChild(subs, path[len-1], direction);
                if (nextChild == null) {
                    nextChild = findEnabledChild(subs, -1, direction);
                }
                if (nextChild != null) {
                    path[len-1] = nextChild;
                    msm.setSelectedPath(path);
		}
	    }
	}

        private void cancel() {
	    // 4234793: This action should call JPopupMenu.firePopupMenuCanceled but it's
	    // a protected method. The real solution could be to make 
	    // firePopupMenuCanceled public and call it directly.
	    JPopupMenu lastPopup = (JPopupMenu)getLastPopup();
	    if (lastPopup != null) {
		lastPopup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
	    }

	    MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath();
	    if(path.length > 4) { /* PENDING(arnaud) Change this to 2 when a mouse grabber is available for MenuBar */
		MenuElement newPath[] = new MenuElement[path.length - 2];
		System.arraycopy(path,0,newPath,0,path.length-2);
		MenuSelectionManager.defaultManager().setSelectedPath(newPath);
	    } else
		MenuSelectionManager.defaultManager().clearSelectedPath();
	}
    }

    private static MenuElement nextEnabledChild(MenuElement e[],
                                                int fromIndex, int toIndex) {
	for (int i=fromIndex; i<=toIndex; i++) {
	    if (e[i] != null) {
		Component comp = e[i].getComponent();
		if (comp != null && comp.isEnabled() && comp.isVisible()) {
                    return e[i];
                }
	    }
	}
	return null;
    }

    private static MenuElement previousEnabledChild(MenuElement e[],
                                                int fromIndex, int toIndex) {
	for (int i=fromIndex; i>=toIndex; i--) {
	    if (e[i] != null) {
		Component comp = e[i].getComponent();
		if (comp != null && comp.isEnabled() && comp.isVisible()) {
                    return e[i];
                }
	    }
	}
	return null;
    }

    static MenuElement findEnabledChild(MenuElement e[], int fromIndex,
                                                boolean forward) {
        MenuElement result = null;
        if (forward) {
            result = nextEnabledChild(e, fromIndex+1, e.length-1);
            if (result == null) result = nextEnabledChild(e, 0, fromIndex-1);
        } else {
            result = previousEnabledChild(e, fromIndex-1, 0);
            if (result == null) result = previousEnabledChild(e, e.length-1,
                                                              fromIndex+1);
        }
	return result;
    }

    static MenuElement findEnabledChild(MenuElement e[],
                                   MenuElement elem, boolean forward) {
        for (int i=0; i<e.length; i++) {
            if (e[i] == elem) {
                return findEnabledChild(e, i, forward);
            }
        }
        return null;
    }

    private transient static MouseGrabber mouseGrabber = null;

    private static class MouseGrabber implements ChangeListener,
        AWTEventListener, ComponentListener, WindowListener {

        Window grabbedWindow;
	MenuElement[] lastPathSelected;

        public MouseGrabber() {
	    MenuSelectionManager msm = MenuSelectionManager.defaultManager();
	    msm.addChangeListener(this);
            this.lastPathSelected = msm.getSelectedPath();
            if(this.lastPathSelected.length != 0) {
                grabWindow(this.lastPathSelected);
            }
        }

        void grabWindow(MenuElement[] newPath) {
            // A grab needs to be added
            java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction() {
                    public Object run() {
                        Toolkit.getDefaultToolkit()
                            .addAWTEventListener(MouseGrabber.this,
                                AWTEvent.MOUSE_EVENT_MASK |
                                AWTEvent.MOUSE_MOTION_EVENT_MASK |
                                AWTEvent.MOUSE_WHEEL_EVENT_MASK);
                        return null;
                    }
                }
            );

            Component invoker = newPath[0].getComponent();
            if (invoker instanceof JPopupMenu) {
                invoker = ((JPopupMenu)invoker).getInvoker();
            }
            grabbedWindow = invoker instanceof Window?
                    (Window)invoker :
                    SwingUtilities.getWindowAncestor(invoker);
            if(grabbedWindow != null) {
                grabbedWindow.addComponentListener(this);
                grabbedWindow.addWindowListener(this);
            }
        }

        void ungrabWindow() {
            // The grab should be removed
             java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction() {
                    public Object run() {
                        Toolkit.getDefaultToolkit()
                            .removeAWTEventListener(MouseGrabber.this);
                        return null;
                    }
                }
            );
            if(grabbedWindow != null) {
                grabbedWindow.removeComponentListener(this);
                grabbedWindow.removeWindowListener(this);
                grabbedWindow = null;
            }
        }

	public void stateChanged(ChangeEvent e) {
	    MenuSelectionManager msm = MenuSelectionManager.defaultManager();
	    MenuElement[] p = msm.getSelectedPath();

	    if (lastPathSelected.length == 0 && p.length != 0) {
                grabWindow(p);
            }

	    if (lastPathSelected.length != 0 && p.length == 0) {
                ungrabWindow();
            }

            lastPathSelected = p;
        }

        public void eventDispatched(AWTEvent ev) {
            switch (ev.getID()) {
            case MouseEvent.MOUSE_PRESSED:
                Component src = (Component)ev.getSource();
                if (isInPopup(src) ||
                    (src instanceof JMenu && ((JMenu)src).isSelected())) {
                    return;
                }
                if (!(src instanceof JComponent) ||
                   ! (((JComponent)src).getClientProperty("doNotCancelPopup")
                         == BasicComboBoxUI.HIDE_POPUP_KEY)) {
                    // Cancel popup only if this property was not set.
                    // If this property is set to TRUE component wants
                    // to deal with this event by himself.
                    cancelPopupMenu();
                    // Ask UIManager about should we consume event that closes
                    // popup. This made to match native apps behaviour.
                    boolean consumeEvent =
                        UIManager.getBoolean("PopupMenu.consumeEventOnClose");
                    // Consume the event so that normal processing stops.
                    if(consumeEvent && !(src instanceof MenuElement)) {
                        ((MouseEvent)ev).consume();
                    }
                }
                break;

            case MouseEvent.MOUSE_RELEASED:
                src = (Component)ev.getSource();
                if(src instanceof JMenu || !(src instanceof JMenuItem)) {
                    MenuSelectionManager.defaultManager().
                        processMouseEvent((MouseEvent)ev);
                }
                break;
            case MouseEvent.MOUSE_DRAGGED:
                MenuSelectionManager.defaultManager().
                    processMouseEvent((MouseEvent)ev);
                break;
            case MouseEvent.MOUSE_WHEEL:
                if (isInPopup((Component)ev.getSource())) {
                    return;
                }
                cancelPopupMenu();
                break;
            }
        }

        boolean isInPopup(Component src) {
            for (Component c=src; c!=null; c=c.getParent()) {
                if (c instanceof Applet || c instanceof Window) {
                    break;
                } else if (c instanceof JPopupMenu) {
                    return true;
                }
            }
            return false;
        }

        void cancelPopupMenu() {
	    JPopupMenu firstPopup = (JPopupMenu)getFirstPopup();
            // 4234793: This action should call firePopupMenuCanceled but it's
            // a protected method. The real solution could be to make 
            // firePopupMenuCanceled public and call it directly.
            List popups = getPopups();
            Iterator iter = popups.iterator();
            while (iter.hasNext()) {
                JPopupMenu popup = (JPopupMenu)iter.next();
                popup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
            }
            MenuSelectionManager.defaultManager().clearSelectedPath();
        }

        public void componentResized(ComponentEvent e) {
            cancelPopupMenu();
        }
        public void componentMoved(ComponentEvent e) {
            cancelPopupMenu();
        }
        public void componentShown(ComponentEvent e) {
            cancelPopupMenu();
        }
        public void componentHidden(ComponentEvent e) {
            cancelPopupMenu();
        }
        public void windowClosing(WindowEvent e) {
            cancelPopupMenu();
        }
        public void windowClosed(WindowEvent e) {
            cancelPopupMenu();
        }
        public void windowIconified(WindowEvent e) {
            cancelPopupMenu();
        }
        public void windowDeactivated(WindowEvent e) {
            if(doUnpostPopupOnDeactivation()) {
                cancelPopupMenu();
            }
        }
        public void windowOpened(WindowEvent e) {}
        public void windowDeiconified(WindowEvent e) {}
        public void windowActivated(WindowEvent e) {}
    }

    /**
     * This helper is added to MenuSelectionManager as a ChangeListener to 
     * listen to menu selection changes. When a menu is activated, it passes
     * focus to its parent JRootPane, and installs an ActionMap/InputMap pair
     * on that JRootPane. Those maps are necessary in order for menu
     * navigation to work. When menu is being deactivated, it restores focus
     * to the component that has had it before menu activation, and uninstalls
     * the maps.
     * This helper is also installed as a KeyListener on root pane when menu
     * is active. It forwards key events to MenuSelectionManager for mnemonic
     * keys handling.
     */
    private static class MenuKeyboardHelper
        implements ChangeListener, KeyListener {

        private Component lastFocused = null;
  	private MenuElement[] lastPathSelected = new MenuElement[0];
        private JPopupMenu lastPopup;

        private JRootPane invokerRootPane;
        private ActionMap menuActionMap = getActionMap();
        private InputMap menuInputMap;
        private boolean focusTraversalKeysEnabled;

        /*
         * Fix for 4213634
         * If this is false, KEY_TYPED and KEY_RELEASED events are NOT
         * processed. This is needed to avoid activating a menuitem when
         * the menu and menuitem share the same mnemonic.
         */
        private boolean receivedKeyPressed = false;

        void removeItems() {
            if (lastFocused != null) {
                if(!lastFocused.requestFocusInWindow()) {
                    // Workarounr for 4810575.
                    // If lastFocused is not in currently focused window
                    // requestFocusInWindow will fail. In this case we must
                    // request focus by requestFocus() if it was not
                    // transferred from our popup.
                    Window cfw = KeyboardFocusManager
                                 .getCurrentKeyboardFocusManager()
                                  .getFocusedWindow();
                    if(cfw != null &&
                       "###focusableSwingPopup###".equals(cfw.getName())) {
                        lastFocused.requestFocus();
                    }

                }
                lastFocused = null;
            }
            if (invokerRootPane != null) {
                invokerRootPane.removeKeyListener(menuKeyboardHelper);
                invokerRootPane.setFocusTraversalKeysEnabled(focusTraversalKeysEnabled);
                removeUIInputMap(invokerRootPane, menuInputMap);
                removeUIActionMap(invokerRootPane, menuActionMap);
                invokerRootPane = null;
            } 
            receivedKeyPressed = false;
        }

        private FocusListener rootPaneFocusListener = new FocusAdapter() {
                public void focusGained(FocusEvent ev) {
                    Component opposite = ev.getOppositeComponent();
                    if (opposite != null) {
                        lastFocused = opposite;
                    }
                    ev.getComponent().removeFocusListener(this);
                }
            };

        /**
         * Return the last JPopupMenu in <code>path</code>,
         * or <code>null</code> if none found
         */
        JPopupMenu getActivePopup(MenuElement[] path) {
            for (int i=path.length-1; i>=0; i--) {
                MenuElement elem = path[i];
                if (elem instanceof JPopupMenu) {
                    return (JPopupMenu)elem;
                }
            }
            return null;
        }

        void addUIInputMap(JComponent c, InputMap map) {
            InputMap lastNonUI = null;
            InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);

            while (parent != null && !(parent instanceof UIResource)) {
                lastNonUI = parent;
                parent = parent.getParent();
            }

            if (lastNonUI == null) {
                c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map);
            } else {
                lastNonUI.setParent(map);
            }
            map.setParent(parent);
        }

        void addUIActionMap(JComponent c, ActionMap map) {
            ActionMap lastNonUI = null;
            ActionMap parent = c.getActionMap();

            while (parent != null && !(parent instanceof UIResource)) {
                lastNonUI = parent;
                parent = parent.getParent();
            }

            if (lastNonUI == null) {
                c.setActionMap(map);
            } else {
                lastNonUI.setParent(map);
            }
            map.setParent(parent);
        }

        void removeUIInputMap(JComponent c, InputMap map) {
            InputMap im = null;
            InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);

            while (parent != null) {
                if (parent == map) {
                    if (im == null) {
                        c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW,
                                      map.getParent());
                    } else {
                        im.setParent(map.getParent());
                    }
                    break;
                }
                im = parent;
                parent = parent.getParent();
            }
        }

        void removeUIActionMap(JComponent c, ActionMap map) {
            ActionMap im = null;
            ActionMap parent = c.getActionMap();

            while (parent != null) {
                if (parent == map) {
                    if (im == null) {
                        c.setActionMap(map.getParent());
                    } else {
                        im.setParent(map.getParent());
                    }
                    break;
                }
                im = parent;
                parent = parent.getParent();
            }
        }

        public void stateChanged(ChangeEvent ev) {
            if (!(UIManager.getLookAndFeel() instanceof BasicLookAndFeel)) {
                MenuSelectionManager msm = MenuSelectionManager.
                                           defaultManager();
                msm.removeChangeListener(this);
                menuKeyboardHelperInstalled = false;
                return;
            }
	    MenuSelectionManager msm = (MenuSelectionManager)ev.getSource();
	    MenuElement[] p = msm.getSelectedPath();
            JPopupMenu popup = getActivePopup(p);
            if (popup != null && !popup.isFocusable()) {
                // Do nothing for non-focusable popups
                return;
            }

            if   (lastPathSelected.length != 0 && p.length != 0 ) {
                if (!checkInvokerEqual(p[0],lastPathSelected[0])) {
                        removeItems();
                        lastPathSelected = new MenuElement[0];
                        }

        	}




	    if (lastPathSelected.length == 0 && p.length > 0) {
                // menu posted
                JComponent invoker;

                if (popup == null) {
                    if (p.length == 2 && p[0] instanceof JMenuBar &&
                        p[1] instanceof JMenu) {
                        // a menu has been selected but not open
                        invoker = (JComponent)p[1];
                        popup = ((JMenu)invoker).getPopupMenu();
                    } else {
                        return;
                    }
                } else {
                    Component c = popup.getInvoker();
                    if(c instanceof JFrame) {
                        invoker = ((JFrame)c).getRootPane();
                    } else if(c instanceof JApplet) {
                        invoker = ((JApplet)c).getRootPane();
                    } else {
                        while (!(c instanceof JComponent)) {
                            if (c == null) {
                                return;
                            }
                            c = c.getParent();
                        }
                        invoker = (JComponent)c;
                    }
                }

                // remember current focus owner
                lastFocused = KeyboardFocusManager.
                    getCurrentKeyboardFocusManager().getFocusOwner();

                // request focus on root pane and install keybindings
                // used for menu navigation
                invokerRootPane = SwingUtilities.getRootPane(invoker);
                if (invokerRootPane != null) {
                    invokerRootPane.addFocusListener(rootPaneFocusListener);
                    invokerRootPane.requestFocus(true);
                    invokerRootPane.addKeyListener(menuKeyboardHelper);
                    focusTraversalKeysEnabled = invokerRootPane.
                                      getFocusTraversalKeysEnabled();
                    invokerRootPane.setFocusTraversalKeysEnabled(false);

                    menuInputMap = getInputMap(popup, invokerRootPane);
                    addUIInputMap(invokerRootPane, menuInputMap);
                    addUIActionMap(invokerRootPane, menuActionMap);
                }
            } else if (lastPathSelected.length != 0 && p.length == 0) {
		// menu hidden -- return focus to where it had been before
                // and uninstall menu keybindings
                   removeItems();
	    } else {
                if (popup != lastPopup) {
                    receivedKeyPressed = false;
                }
            }

            // Remember the last path selected
            lastPathSelected = p;
            lastPopup = popup;
        }

        public void keyPressed(KeyEvent ev) {
            receivedKeyPressed = true;
            MenuSelectionManager.defaultManager().processKeyEvent(ev);
        }

        public void keyReleased(KeyEvent ev) {
	    if (receivedKeyPressed) {
		receivedKeyPressed = false;
                MenuSelectionManager.defaultManager().processKeyEvent(ev);
            }
        }

        public void keyTyped(KeyEvent ev) {
	    if (receivedKeyPressed) {
                MenuSelectionManager.defaultManager().processKeyEvent(ev);
            }
        }
    }
}