Package javax.swing.plaf.basic

Source Code of javax.swing.plaf.basic.MenuKeyBindingProcessor$CancelEventFireStarter

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
/**
* @author Alexander T. Simbirtsev
*/
package javax.swing.plaf.basic;

import java.awt.Component;
import java.awt.KeyEventDispatcher;
import java.awt.Toolkit;
import java.awt.AWTEvent;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.AWTEventListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultFocusManager;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;

import org.apache.harmony.x.swing.Utilities;


class MenuKeyBindingProcessor implements KeyEventDispatcher {

    private abstract static class GenericNavigationAction extends AbstractAction {
        protected static final int FORWARD = 1;
        protected static final int BACKWARD = -1;

        public GenericNavigationAction(final String name) {
            super(name);
        }

        protected final int getPopupIndex(final MenuElement[] path, final JPopupMenu popup) {
            int index = path.length - 1;
            while(index >= 0 && path[index] != popup) {
                index--;
            }
            return index;
        }

        protected final MenuElement[] derivePath(final MenuElement[] path,
                                                 final int length) {
            return derivePath(path, length, length);
        }

        protected final MenuElement[] derivePath(final MenuElement[] path,
                                           final int newLength,
                                           final int copyLength) {

            final MenuElement[] result = new MenuElement[newLength];
            System.arraycopy(path, 0, result, 0, copyLength);
            return result;
        }

        protected final MenuElement[] getSelectedPath() {
            return MenuSelectionManager.defaultManager().getSelectedPath();
        }

        protected final void setSelectedPath(final MenuElement[] path) {
            MenuSelectionManager.defaultManager().setSelectedPath(path);
        }
    }

    private abstract static class ChildParentAction extends GenericNavigationAction {
        private final int direction;

        public ChildParentAction(final String name, final int direction) {
            super(name);
            this.direction = direction;
        }

        protected void selectNextTopLevelMenu(final MenuElement[] path) {
            if (Utilities.isEmptyArray(path) || !(path[0] instanceof JMenuBar)) {
                return;
            }
            final JMenuBar menuBar = (JMenuBar)path[0];
            final JMenu menu = (JMenu)path[1];
            final int menuIndex = menuBar.getComponentIndex(menu);
            final JMenu nextMenu = getNextMenu(menuBar, menuIndex);
            if (nextMenu != null && nextMenu != menu) {
                setSelectedPath(new MenuElement[] {menuBar, nextMenu,
                                                   nextMenu.getPopupMenu()});
            }
        }

        private JMenu getNextMenu(final JMenuBar menuBar, final int index) {
            final int numElements = menuBar.getMenuCount();
            for (int i = 1; i <= numElements; i++) {
                int j = (direction > 0 ? index + i : numElements + index - i) % numElements;
                final JMenu menu = menuBar.getMenu(j);
                if (menu != null) {
                    return menu;
                }
            }
            return null;
        }
    }

    private static final Action SELECT_PARENT_ACTION = new ChildParentAction("selectParent", GenericNavigationAction.BACKWARD) {
        public void actionPerformed(final ActionEvent e) {
            final JPopupMenu popup = (JPopupMenu)e.getSource();
            final MenuElement[] path = getSelectedPath();
            if (path.length > 3) {
                int index = getPopupIndex(path, popup);
                JMenu menu = (JMenu)path[index - 1];
                if (!menu.isTopLevelMenu()) {
                    setSelectedPath(derivePath(path, index));
                    return;
                }
            }
            selectNextTopLevelMenu(path);
        }
    };

    private static final Action SELECT_CHILD_ACTION = new ChildParentAction("selectChild", GenericNavigationAction.FORWARD) {
        public void actionPerformed(final ActionEvent e) {
            final MenuElement[] path = getSelectedPath();
            if (path.length > 3 || path[0] instanceof JPopupMenu) {
                final MenuElement lastElement = path[path.length - 1];
                final MenuElement[] subElements = lastElement.getSubElements();
                if (!Utilities.isEmptyArray(subElements)) {
                    MenuElement[] newPath;
                    final MenuElement newSelectedItem = subElements[0];
                    final MenuElement newSelectedChild = Utilities.getFirstSelectableItem(newSelectedItem.getSubElements());
                    if (newSelectedItem instanceof JPopupMenu && newSelectedChild != null) {
                        newPath = derivePath(path, path.length + 2, path.length);
                        newPath[path.length + 1] = newSelectedChild;
                    } else {
                        newPath = derivePath(path, path.length + 1, path.length);
                    }
                    newPath[path.length] = newSelectedItem;
                    setSelectedPath(newPath);
                    return;
                }
            }
            selectNextTopLevelMenu(path);
        }
    };

    private static class PrevNextAction extends GenericNavigationAction {
        private final int direction;

        public PrevNextAction(final String name, final int direction) {
            super(name);
            this.direction = direction;
        }

        public void actionPerformed(final ActionEvent e) {
            final JPopupMenu popup = (JPopupMenu)e.getSource();
            final MenuElement[] path = getSelectedPath();
            int index = getPopupIndex(path, popup);
            if (popup.getComponentCount() != 0) {
                final MenuElement curSelection = (index != path.length - 1) ? path[path.length - 1] : null;
                setPath(path, index, curSelection, popup);
            } else {
                index = getNextPopupIndex(path, --index);
                if (index >= 0) {
                    setPath(path, index, path[index + 1], popup);
                }
            }
        }

        private int getNextPopupIndex(final MenuElement[] path, final int startIndex) {
            int index = startIndex;
            while(index >= 0 && !(path[index] instanceof JPopupMenu)) {
                index--;
            }
            return index;
        }

        private void setPath(final MenuElement[] path, final int index,
                             final MenuElement curSelection, final JPopupMenu menu) {
            final MenuElement nextMenuItem = getNextMenuItem(menu, curSelection);
            if (nextMenuItem != null) {
                final MenuElement[] newPath = derivePath(path, index + 2, index + 1);
                newPath[newPath.length - 1] = nextMenuItem;
                setSelectedPath(newPath);
            }
        }

        private MenuElement getNextMenuItem(final JPopupMenu menu, final MenuElement curSelection) {
            if (curSelection == null) {
                int startIndex = (direction == FORWARD) ? menu.getComponentCount() - 1 : 0;
                return getNextMenuItem(menu, startIndex);
            }
            final int startIndex = getSelectionIndex(menu, curSelection);
            if (startIndex >= 0) {
                return getNextMenuItem(menu, startIndex);
            }
            return null;
        }

        private MenuElement getNextMenuItem(final JPopupMenu menu, final int index) {
            final int numElements = menu.getComponentCount();
            for (int i = 1; i <= numElements; i++) {
                int j = ((direction > 0) ? index + i : numElements + index - i) % numElements;
                final Component menuComponent = menu.getComponent(j);
                if (menuComponent.isVisible() && menuComponent.isEnabled()
                    && menuComponent instanceof MenuElement) {

                    return (MenuElement)menuComponent;
                }
            }
            return null;
        }

        private int getSelectionIndex(final JPopupMenu menu, final MenuElement selection) {
            int numElements = menu.getComponentCount();
            for (int i = 0; i < numElements; i++) {
                if (menu.getComponent(i) == selection) {
                    return i;
                }
            }
            return -1;
        }
    }

    private static final Action SELECT_NEXT_ACTION = new PrevNextAction("selectNext", PrevNextAction.FORWARD);
    private static final Action SELECT_PREVIOUS_ACTION = new PrevNextAction("selectPrevious", PrevNextAction.BACKWARD);

    private static final class CancelEventFireStarter extends JPopupMenu {
        private static final Class[] NO_CLASSES_NO_ARGUMENTS = new Class[0];

        private Method fireCanceled = null;

        public CancelEventFireStarter() {
            try {
                fireCanceled = JPopupMenu.class.getDeclaredMethod("firePopupMenuCanceled", NO_CLASSES_NO_ARGUMENTS);
                fireCanceled.setAccessible(true);
            } catch (SecurityException e) {
            } catch (NoSuchMethodException e) {}
        }

        public void firePopupMenuCanceled(final JPopupMenu popup) {
            if (fireCanceled == null) {
                return;
            }
            try {
                fireCanceled.invoke(popup, (Object[])NO_CLASSES_NO_ARGUMENTS);
            } catch (IllegalArgumentException e) {
            } catch (IllegalAccessException e) {
            } catch (InvocationTargetException e) {}
        }
    }

    private static final Action CANCEL_ACTION = new GenericNavigationAction("cancel") {
        private final CancelEventFireStarter cancelEventFireStarter = new CancelEventFireStarter();

        public void actionPerformed(final ActionEvent e) {
            final JPopupMenu popup = (JPopupMenu)e.getSource();
            cancelEventFireStarter.firePopupMenuCanceled(popup);

            final MenuElement[] path = getSelectedPath();
            if (path.length > 4) {
                setSelectedPath(derivePath(path, path.length - 2));
            } else {
                setSelectedPath(null);
            }
        }
    };

    private static final Action RETURN_ACTION = new GenericNavigationAction("return") {
        public void actionPerformed(final ActionEvent e) {
            final MenuElement[] path = getSelectedPath();
            if (Utilities.isEmptyArray(path)) {
                return;
            }
            final MenuElement selection = path[path.length - 1];
            if (selection instanceof JMenuItem) {
                MenuSelectionManager.defaultManager().clearSelectedPath();
                final JMenuItem menuItem = (JMenuItem)selection;
                menuItem.doClick(0);
            }
        }
    };

    private final HashMap actionMap = new HashMap();

    private static int numInstallations;
    private static MenuKeyBindingProcessor sharedInstance;

    private MenuKeyBindingProcessor() {
        installActions();
    }

    public static void attach() {
        if (sharedInstance == null) {
            sharedInstance = new MenuKeyBindingProcessor();
        }
        if (numInstallations == 0) {
            DefaultFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(sharedInstance);
        }
        numInstallations++;
    }

    public static void detach() {
        if (numInstallations == 1) {
            DefaultFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(sharedInstance);
        }
        if (numInstallations > 0) {
            numInstallations--;
        }
    }

    public boolean dispatchKeyEvent(final KeyEvent e) {
        if (e.getID() != KeyEvent.KEY_PRESSED || e.isConsumed()) {
            return false;
        }

        // dispatch event to user listeners
        for (AWTEventListener listener :
                Toolkit.getDefaultToolkit().getAWTEventListeners(
                    AWTEvent.KEY_EVENT_MASK)) {
            listener.eventDispatched(e);
        }
       
        if (e.isConsumed()) {
            // consumed by user listener
            return true;
        }
       
        final JPopupMenu activePopupMenu = getActivePopupMenu();
        if (activePopupMenu == null) {
            return false;
        }
        final KeyStroke ks = KeyStroke.getKeyStrokeForEvent(e);
        final Object actionKey = activePopupMenu.getInputMap().get(ks);
        final Action action = (Action)actionMap.get(actionKey);
        if (action == null) {
            return false;
        }
       
        SwingUtilities.notifyAction(action, ks, e, activePopupMenu, e.getModifiersEx());
        return true;
    }

    private JPopupMenu getActivePopupMenu() {
        final MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
        if (Utilities.isEmptyArray(path)) {
            return null;
        }

        if (!Utilities.isValidFirstPathElement(path[0])) {
            return null;
        }

        for (int i = path.length - 1; i >= 0; i--) {
            if (path[i] instanceof JPopupMenu) {
                return (JPopupMenu)path[i];
            }
        }

        return null;
    }

    private void installActions() {
        addAction(actionMap, SELECT_CHILD_ACTION);
        addAction(actionMap, SELECT_PARENT_ACTION);
        addAction(actionMap, SELECT_PREVIOUS_ACTION);
        addAction(actionMap, SELECT_NEXT_ACTION);
        addAction(actionMap, RETURN_ACTION);
        addAction(actionMap, CANCEL_ACTION);
    }

    private void addAction(final HashMap actionMap, final Action action) {
        actionMap.put(action.getValue(Action.NAME), action);
    }

}
TOP

Related Classes of javax.swing.plaf.basic.MenuKeyBindingProcessor$CancelEventFireStarter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.