- /*
- * @(#)BasicPopupMenuUI.java 1.119 04/04/16
- *
- * Copyright 2004 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.119 04/16/04
- * @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;
-
- 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);
- }
- }
-
- 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;
- }
-
- 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) {
- 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);
- }
- }
- }
- }