All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.javafx.scene.KeyboardShortcutsHandler Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.scene;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.event.Event;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.Mnemonic;

import com.sun.javafx.PlatformUtil;
import com.sun.javafx.collections.ObservableListWrapper;
import com.sun.javafx.collections.ObservableMapWrapper;
import com.sun.javafx.event.BasicEventDispatcher;
import com.sun.javafx.scene.traversal.Direction;

import static javafx.scene.input.KeyCode.DOWN;
import static javafx.scene.input.KeyCode.LEFT;
import static javafx.scene.input.KeyCode.RIGHT;
import static javafx.scene.input.KeyCode.TAB;
import static javafx.scene.input.KeyCode.UP;

public final class KeyboardShortcutsHandler extends BasicEventDispatcher {
    private ObservableMap accelerators;
    private ObservableMap> mnemonics;

    public void addMnemonic(Mnemonic m) {
        ObservableList mnemonicsList = (ObservableList)getMnemonics().get(m.getKeyCombination());
        if (mnemonicsList == null) {
            mnemonicsList = new ObservableListWrapper(new ArrayList());
            getMnemonics().put(m.getKeyCombination(), mnemonicsList);
        }
        boolean foundMnemonic = false;
        for (int i = 0 ; i < mnemonicsList.size() ; i++) {
            if (mnemonicsList.get(i) == m) {
                foundMnemonic = true;
            }
        }
        if (foundMnemonic == false) {
            mnemonicsList.add(m);
        }
    }

    public void removeMnemonic(Mnemonic m) {
        ObservableList mnemonicsList = (ObservableList)getMnemonics().get(m.getKeyCombination());
        if (mnemonicsList != null) {
            for (int i = 0 ; i < mnemonicsList.size() ; i++) {
                if (mnemonicsList.get(i).getNode() == m.getNode()) {
                    mnemonicsList.remove(i);
                }
            }
        }
    }

    public ObservableMap> getMnemonics() {
        if (mnemonics == null) {
            mnemonics = new ObservableMapWrapper>(new HashMap>());
        }
        return mnemonics;
    }

    public ObservableMap getAccelerators() {
        if (accelerators == null) {
            accelerators = new ObservableMapWrapper(new HashMap());
        }
        return accelerators;
    }

    public void traverse(Node node, Direction dir) {
        node.impl_traverse(dir);
    }

    public void processTraversal(Event event) {
        if (event instanceof KeyEvent && event.getEventType() == KeyEvent.KEY_PRESSED) {
            if (!((KeyEvent)event).isMetaDown() && !((KeyEvent)event).isControlDown() && !((KeyEvent)event).isAltDown()) {
                Object obj = event.getTarget();
                if (obj instanceof Node) {
                
                    switch (((KeyEvent)event).getCode()) {
                      case TAB :
                          if (((KeyEvent)event).isShiftDown()) {
                              traverse(((Node)obj), com.sun.javafx.scene.traversal.Direction.PREVIOUS);
                          }
                          else {
                              traverse(((Node)obj), com.sun.javafx.scene.traversal.Direction.NEXT);
                          }
                          event.consume();
                          break;
                      case UP :
                          traverse(((Node)obj), com.sun.javafx.scene.traversal.Direction.UP);
                          event.consume();
                          break;
                      case DOWN :
                          traverse(((Node)obj), com.sun.javafx.scene.traversal.Direction.DOWN);
                          event.consume();
                          break;
                      case LEFT :
                          traverse(((Node)obj), com.sun.javafx.scene.traversal.Direction.LEFT);
                          event.consume();
                          break;
                      case RIGHT :
                          traverse(((Node)obj), com.sun.javafx.scene.traversal.Direction.RIGHT);
                          event.consume();
                          break;
                      default :
                          break;
                    }
                }
            }
        }
    }

    @Override
    public Event dispatchBubblingEvent(Event event) {
        /*
        ** If the key event hasn't been consumed then
        ** we will process global events in the order :
        **    . Mnemonics,
        **    . Accelerators,
        **    . Navigation.
        ** This processing is extra to that of listeners and
        ** the focus Node.
        */
        if (event.getEventType() == KeyEvent.KEY_PRESSED) {
            if (PlatformUtil.isMac()) {
                if (((KeyEvent)event).isMetaDown()) {
                    processMnemonics((KeyEvent)event);
                }
            } else {
                if (((KeyEvent)event).isAltDown() || isMnemonicsDisplayEnabled()) {
                    processMnemonics((KeyEvent)event);
                }
            }

            if (!event.isConsumed()) {
                processAccelerators((KeyEvent)event);
            }

            if (!event.isConsumed()) {
                processTraversal(event);
            }
        }

        /*
        ** if we're not on mac, and nobody consumed the event, then we should
        ** check to see if we should highlight the mnemonics on the scene
        */
        if (!PlatformUtil.isMac()) {
            if (event.getEventType() == KeyEvent.KEY_PRESSED) {
                if (((KeyEvent)event).isAltDown()  && !event.isConsumed()) {
                    /*
                    ** show mnemonics while alt is held
                    */
                    if (!isMnemonicsDisplayEnabled()) {
                        setMnemonicsDisplayEnabled(true);
                    }
                    else {
                        if (PlatformUtil.isWindows()) {
                            setMnemonicsDisplayEnabled(!isMnemonicsDisplayEnabled());
                        }
                    }
                }
            }
            if (event.getEventType() == KeyEvent.KEY_RELEASED) {
                if (!((KeyEvent)event).isAltDown()) {
                    if (!PlatformUtil.isWindows()) {
                        setMnemonicsDisplayEnabled(false);
                    }
                }
            }
        }
        return event;
    }

    private void processMnemonics(KeyEvent event) {
        if (mnemonics != null) {

            ObservableList mnemonicsList = null;

            for (Map.Entry>
                    mnemonic: mnemonics.entrySet()) {

                if (!isMnemonicsDisplayEnabled()) {
                    if (mnemonic.getKey().match(event)) {
                        mnemonicsList = (ObservableList) mnemonic.getValue();
                        break;
                    }
                }
                else {
                    /*
                    ** Mnemonics display has been enabled, which means
                    ** we act as is the alt key is being held down.
                    */

                    KeyEvent fakeEvent = new KeyEvent(null, event.getTarget(), KeyEvent.KEY_PRESSED,
                                                                event.getCharacter(),
                                                                event.getText(),
                                                                event.getCode(),
                                                                event.isShiftDown(),
                                                                event.isControlDown(),
                                                                true,
                                                                event.isMetaDown());


                    if (mnemonic.getKey().match(fakeEvent)) {
                        mnemonicsList = (ObservableList) mnemonic.getValue();
                        break;
                    }
                }
            }

            if (mnemonicsList != null) {
                /*
                ** for mnemonics we need to check if visible and reachable....
                ** if a single Mnemonic on the keyCombo we
                ** fire the runnable in Mnemoninic, and transfer focus
                ** if there is more than one then we just
                ** transfer the focus
                **
                */
                boolean multipleNodes = false;
                Node firstNode = null;
                Mnemonic firstMnemonics = null;
                int focusedIndex = -1;
                int nextFocusable = -1;

                /*
                ** find first focusable node
                */
                for (int i = 0 ; i < mnemonicsList.size() ; i++) {
                    if (mnemonicsList.get(i) instanceof Mnemonic) {
                        Node currentNode = (Node)mnemonicsList.get(i).getNode();

                        if (firstMnemonics == null && (currentNode.impl_isTreeVisible() && !currentNode.isDisabled())) {
                            firstMnemonics = mnemonicsList.get(i);
                        }

                        if (currentNode.impl_isTreeVisible() && (currentNode.isFocusTraversable() && !currentNode.isDisabled())) {
                            if (firstNode == null) {
                                firstNode = currentNode;
                            }
                            else {
                                /*
                                ** there is more than one node on this keyCombo
                                */
                                multipleNodes = true;
                                if (focusedIndex != -1) {
                                    if (nextFocusable == -1) {
                                        nextFocusable = i;
                                    }
                                }
                            }
                        }
                        /*
                        ** one of our targets has the focus already
                        */
                        if (currentNode.isFocused()) {
                            focusedIndex = i;
                        }
                    }
                }

                if (firstNode != null) {
                    if (!multipleNodes == true) {
                        /*
                        ** just one target
                        */
                        firstNode.requestFocus();
                        event.consume();
                    }
                    else {
                        /*
                        ** we have multiple nodes using the same mnemonic.
                        ** this is allowed for nmemonics, and we simple
                        ** focus traverse between them
                        */
                        if (focusedIndex == -1) {
                            firstNode.requestFocus();
                            event.consume();
                        }
                        else {
                            if (focusedIndex >= mnemonicsList.size()) {
                                firstNode.requestFocus();
                                event.consume();
                            }
                            else {
                                if (nextFocusable != -1) {
                                    ((Node)mnemonicsList.get(nextFocusable).getNode()).requestFocus();
                                }
                                else {
                                    firstNode.requestFocus();
                                }
                                event.consume();
                            }
                        }
                    }
                }
                if (!multipleNodes && firstMnemonics != null) {
                    firstMnemonics.fire();
                }
            }
        }
    }

    private void processAccelerators(KeyEvent event) {
        if (accelerators != null) {
            for (Map.Entry
                     accelerator: accelerators.entrySet()) {
                
                if (accelerator.getKey().match(event)) {
                    Runnable acceleratorRunnable = accelerator.getValue();
                    if (acceleratorRunnable != null) {
                        /*
                        ** for accelerators there can only be one target
                        ** and we don't care whether it's visible or reachable....
                        ** we just run the Runnable.......
                        */
                        acceleratorRunnable.run();
                        event.consume();
                    }
                }
            }
        }
    }

    private void processMnemonicsKeyDisplay() {
        ObservableList mnemonicsList = null;
        if (mnemonics != null) {
            for (Map.Entry> mnemonic: mnemonics.entrySet()) {
                mnemonicsList = (ObservableList) mnemonic.getValue();
         
                if (mnemonicsList != null) {
                    for (int i = 0 ; i < mnemonicsList.size() ; i++) {
                        Node currentNode = (Node)mnemonicsList.get(i).getNode();
                        currentNode.impl_setShowMnemonics(mnemonicsDisplayEnabled);
                    }
                }
            }
        }
    }

    /*
    ** remember if the alt key is being held
    */
    private boolean mnemonicsDisplayEnabled = false;

    public boolean isMnemonicsDisplayEnabled() {
        return mnemonicsDisplayEnabled;
    }
    public void setMnemonicsDisplayEnabled(boolean b) {
        if (b != mnemonicsDisplayEnabled) {
            mnemonicsDisplayEnabled = b;
            processMnemonicsKeyDisplay();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy