Java Swing Key Action stringToKeys(String s)

Here you can find the source of stringToKeys(String s)

Description

Convert a space-separated list of Emacs-like key binding names to a list of Swing key strokes.

License

Open Source License

Parameter

Parameter Description
s the string with keys

Return

array of key strokes, or null if the string description is not valid

Declaration

public static KeyStroke[] stringToKeys(String s) 

Method Source Code

//package com.java2s;
/*//from  w  w w . j  a v  a  2 s .co m
 * #%L
 * The AIBench Plugin Manager Plugin
 * %%
 * Copyright (C) 2006 - 2016 Daniel Glez-Pe?a and Florentino Fdez-Riverola
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 * #L%
 */

import java.awt.GraphicsEnvironment;

import java.awt.Toolkit;

import java.awt.event.KeyEvent;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

import javax.swing.KeyStroke;

public class Main {
    private static final int CTRL_WILDCARD_MASK = 32768;
    private static final int ALT_WILDCARD_MASK = CTRL_WILDCARD_MASK * 2;
    /** Operating system is Windows NT. */
    public static final int OS_WINNT = 1 << 0;
    /** Operating system is Windows 95. */
    public static final int OS_WIN95 = OS_WINNT << 1;
    /** Operating system is Windows 98. */
    public static final int OS_WIN98 = OS_WIN95 << 1;
    /** Operating system is Solaris. */
    public static final int OS_SOLARIS = OS_WIN98 << 1;
    /** Operating system is Linux. */
    public static final int OS_LINUX = OS_SOLARIS << 1;
    /** Operating system is HP-UX. */
    public static final int OS_HP = OS_LINUX << 1;
    /** Operating system is IBM AIX. */
    public static final int OS_AIX = OS_HP << 1;
    /** Operating system is SGI IRIX. */
    public static final int OS_IRIX = OS_AIX << 1;
    /** Operating system is Sun OS. */
    public static final int OS_SUNOS = OS_IRIX << 1;
    /** Operating system is Compaq TRU64 Unix */
    public static final int OS_TRU64 = OS_SUNOS << 1;
    /** Operating system is OS/2. */
    public static final int OS_OS2 = OS_TRU64 << 2;
    /** Operating system is Mac. */
    public static final int OS_MAC = OS_OS2 << 1;
    /** Operating system is Windows 2000. */
    public static final int OS_WIN2000 = OS_MAC << 1;
    /** Operating system is Compaq OpenVMS */
    public static final int OS_VMS = OS_WIN2000 << 1;
    /**
     *Operating system is one of the Windows variants but we don't know which
     *one it is
     */
    public static final int OS_WIN_OTHER = OS_VMS << 1;
    /** Operating system is unknown. */
    public static final int OS_OTHER = OS_WIN_OTHER << 1;
    /** Operating system is FreeBSD
     * @since 4.50
     */
    public static final int OS_FREEBSD = OS_OTHER << 1;
    private static int operatingSystem = -1;
    /** reference to map that maps allowed key names to their values (String, Integer)
    and reference to map for mapping of values to their names */
    private static Reference<Object> namesAndValues;

    /** Convert a space-separated list of Emacs-like key binding names to a list of Swing key strokes.
     * @param s the string with keys
     * @return array of key strokes, or <code>null</code> if the string description is not valid
     * @see #stringToKey
     */
    public static KeyStroke[] stringToKeys(String s) {
        StringTokenizer st = new StringTokenizer(
                s.toUpperCase(Locale.ENGLISH), " "); // NOI18N
        ArrayList<KeyStroke> arr = new ArrayList<KeyStroke>();

        while (st.hasMoreElements()) {
            s = st.nextToken();

            KeyStroke k = stringToKey(s);

            if (k == null) {
                return null;
            }

            arr.add(k);
        }

        return arr.toArray(new KeyStroke[arr.size()]);
    }

    /** Construct a new key description from a given universal string
     * description.
     * Provides mapping between Emacs-like textual key descriptions and the
     * <code>KeyStroke</code> object used in Swing.
     * <P>
     * This format has following form:
     * <P><code>[C][A][S][M]-<em>identifier</em></code>
     * <p>Where:
     * <UL>
     * <LI> <code>C</code> stands for the Control key
     * <LI> <code>A</code> stands for the Alt key
     * <LI> <code>S</code> stands for the Shift key
     * <LI> <code>M</code> stands for the Meta key
     * </UL>
     * The format also supports two wildcard codes, to support differences in
     * platforms.  These are the preferred choices for registering keystrokes,
     * since platform conflicts will automatically be handled:
     * <UL>
     * <LI> <code>D</code> stands for the default menu accelerator - the Control
     *  key on most platforms, the Command (meta) key on Macintosh</LI>
     * <LI> <code>O</code> stands for the alternate accelerator - the Alt key on
     *  most platforms, the Ctrl key on Macintosh (Macintosh uses Alt as a
     *  secondary shift key for composing international characters - if you bind
     *  Alt-8 to an action, a mac user with a French keyboard will not be able
     *  to type the <code>[</code> character, which is a significant handicap</LI>
     * </UL>
     * If you use the wildcard characters, and specify a key which will conflict
     * with keys the operating system consumes, it will be mapped to whichever
     * choice can work - for example, on Macintosh, Command-Q is always consumed
     * by the operating system, so <code>D-Q</code> will always map to Control-Q.
     * <p>
     * Every modifier before the hyphen must be pressed.
     * <em>identifier</EM> can be any text constant from {@link KeyEvent} but
     * without the leading <code>VK_</code> characters. So {@link KeyEvent#VK_ENTER} is described as
     * <code>ENTER</code>.
     *
     * @param s the string with the description of the key
     * @return key description object, or <code>null</code> if the string does not represent any valid key
     */
    public static KeyStroke stringToKey(String s) {
        StringTokenizer st = new StringTokenizer(
                s.toUpperCase(Locale.ENGLISH), "-", true); // NOI18N

        int needed = 0;

        HashMap names = initNameAndValues()[0];

        int lastModif = -1;

        try {
            for (;;) {
                String el = st.nextToken();

                // required key
                if (el.equals("-")) { // NOI18N

                    if (lastModif != -1) {
                        needed |= lastModif;
                        lastModif = -1;
                    }

                    continue;
                }

                // if there is more elements
                if (st.hasMoreElements()) {
                    // the text should describe modifiers
                    lastModif = readModifiers(el);
                } else {
                    // last text must be the key code
                    Integer i = (Integer) names.get(el);
                    boolean wildcard = (needed & CTRL_WILDCARD_MASK) != 0;

                    //Strip out the explicit mask - KeyStroke won't know
                    //what to do with it
                    needed = needed & ~CTRL_WILDCARD_MASK;

                    boolean macAlt = (needed & ALT_WILDCARD_MASK) != 0;
                    needed = needed & ~ALT_WILDCARD_MASK;

                    if (i != null) {
                        //#26854 - Default accelerator should be Command on mac
                        if (wildcard) {
                            needed |= getMenuShortCutKeyMask();

                            if ((getOperatingSystem() & OS_MAC) != 0) {
                                if (!usableKeyOnMac(i.intValue(), needed)) {
                                    needed &= ~getMenuShortCutKeyMask();
                                    needed |= KeyEvent.CTRL_MASK;
                                }
                            }
                        }

                        if (macAlt) {
                            if (getOperatingSystem() == OS_MAC) {
                                needed |= KeyEvent.CTRL_MASK;
                            } else {
                                needed |= KeyEvent.ALT_MASK;
                            }
                        }

                        return KeyStroke.getKeyStroke(i.intValue(), needed);
                    } else {
                        return null;
                    }
                }
            }
        } catch (NoSuchElementException ex) {
            return null;
        }
    }

    /** Initialization of the names and values
     * @return array of two hashmaps first maps
     *   allowed key names to their values (String, Integer)
     *  and second
     * hashtable for mapping of values to their names (Integer, String)
     */
    private static synchronized HashMap[] initNameAndValues() {
        if (namesAndValues != null) {
            HashMap[] arr = (HashMap[]) namesAndValues.get();

            if (arr != null) {
                return arr;
            }
        }

        Field[] fields;
        // JW - fix Issue #353-swingx: play nicer inside sandbox.
        try {
            fields = KeyEvent.class.getDeclaredFields();
            //           fields = KeyEvent.class.getFields();
        } catch (SecurityException e) {
            // JW: need to do better? What are the use-cases where we don't have
            // any access to the fields?
            fields = new Field[0];
        }

        HashMap<String, Integer> names = new HashMap<String, Integer>(
                ((fields.length * 4) / 3) + 5, 0.75f);
        HashMap<Integer, String> values = new HashMap<Integer, String>(
                ((fields.length * 4) / 3) + 5, 0.75f);

        for (int i = 0; i < fields.length; i++) {
            if (Modifier.isStatic(fields[i].getModifiers())) {
                String name = fields[i].getName();

                if (name.startsWith("VK_")) { // NOI18N

                    // exclude VK
                    name = name.substring(3);

                    try {
                        int numb = fields[i].getInt(null);
                        Integer value = new Integer(numb);
                        names.put(name, value);
                        values.put(value, name);
                    } catch (IllegalArgumentException ex) {
                    } catch (IllegalAccessException ex) {
                    }
                }
            }
        }

        if (names.get("CONTEXT_MENU") == null) { // NOI18N

            Integer n = new Integer(0x20C);
            names.put("CONTEXT_MENU", n); // NOI18N
            values.put(n, "CONTEXT_MENU"); // NOI18N

            n = new Integer(0x20D);
            names.put("WINDOWS", n); // NOI18N
            values.put(n, "WINDOWS"); // NOI18N
        }

        HashMap[] arr = { names, values };

        namesAndValues = new SoftReference<Object>(arr);

        return arr;
    }

    /** Reads for modifiers and creates integer with required mask.
     * @param s string with modifiers
     * @return integer with mask
     * @exception NoSuchElementException if some letter is not modifier
     */
    private static int readModifiers(String s)
            throws NoSuchElementException {
        int m = 0;

        for (int i = 0; i < s.length(); i++) {
            switch (s.charAt(i)) {
            case 'C':
                m |= KeyEvent.CTRL_MASK;
                break;

            case 'A':
                m |= KeyEvent.ALT_MASK;
                break;

            case 'M':
                m |= KeyEvent.META_MASK;
                break;

            case 'S':
                m |= KeyEvent.SHIFT_MASK;
                break;

            case 'D':
                m |= CTRL_WILDCARD_MASK;
                break;

            case 'O':
                m |= ALT_WILDCARD_MASK;
                break;

            default:
                throw new NoSuchElementException(s);
            }
        }

        return m;
    }

    /**
     * need to guard against headlessExceptions when testing.
     * @return the acceletor mask for shortcuts.
     */
    private static int getMenuShortCutKeyMask() {
        if (GraphicsEnvironment.isHeadless()) {
            return ((getOperatingSystem() & OS_MAC) != 0) ? KeyEvent.META_MASK
                    : KeyEvent.CTRL_MASK;
        }

        return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
    }

    /** Get the operating system on which NetBeans is running.
     * @return one of the <code>OS_*</code> constants (such as {@link #OS_WINNT})
     */
    public static int getOperatingSystem() {
        if (operatingSystem == -1) {
            String osName = System.getProperty("os.name");

            if ("Windows NT".equals(osName)) { // NOI18N
                operatingSystem = OS_WINNT;
            } else if ("Windows 95".equals(osName)) { // NOI18N
                operatingSystem = OS_WIN95;
            } else if ("Windows 98".equals(osName)) { // NOI18N
                operatingSystem = OS_WIN98;
            } else if ("Windows 2000".equals(osName)) { // NOI18N
                operatingSystem = OS_WIN2000;
            } else if (osName.startsWith("Windows ")) { // NOI18N
                operatingSystem = OS_WIN_OTHER;
            } else if ("Solaris".equals(osName)) { // NOI18N
                operatingSystem = OS_SOLARIS;
            } else if (osName.startsWith("SunOS")) { // NOI18N
                operatingSystem = OS_SOLARIS;
            }
            // JDK 1.4 b2 defines os.name for me as "Redhat Linux" -jglick
            else if (osName.endsWith("Linux")) { // NOI18N
                operatingSystem = OS_LINUX;
            } else if ("HP-UX".equals(osName)) { // NOI18N
                operatingSystem = OS_HP;
            } else if ("AIX".equals(osName)) { // NOI18N
                operatingSystem = OS_AIX;
            } else if ("Irix".equals(osName)) { // NOI18N
                operatingSystem = OS_IRIX;
            } else if ("SunOS".equals(osName)) { // NOI18N
                operatingSystem = OS_SUNOS;
            } else if ("Digital UNIX".equals(osName)) { // NOI18N
                operatingSystem = OS_TRU64;
            } else if ("OS/2".equals(osName)) { // NOI18N
                operatingSystem = OS_OS2;
            } else if ("OpenVMS".equals(osName)) { // NOI18N
                operatingSystem = OS_VMS;
            } else if (osName.equals("Mac OS X")) { // NOI18N
                operatingSystem = OS_MAC;
            } else if (osName.startsWith("Darwin")) { // NOI18N
                operatingSystem = OS_MAC;
            } else if (osName.toLowerCase(Locale.US).startsWith("freebsd")) { // NOI18N 
                operatingSystem = OS_FREEBSD;
            } else {
                operatingSystem = OS_OTHER;
            }
        }
        return operatingSystem;
    }

    private static boolean usableKeyOnMac(int key, int mask) {
        //All permutations fail for Q except ctrl
        if (key == KeyEvent.VK_Q) {
            return false;
        }

        boolean isMeta = ((mask & KeyEvent.META_MASK) != 0)
                || ((mask & KeyEvent.CTRL_DOWN_MASK) != 0);

        boolean isAlt = ((mask & KeyEvent.ALT_MASK) != 0)
                || ((mask & KeyEvent.ALT_DOWN_MASK) != 0);

        boolean isOnlyMeta = isMeta
                && ((mask & ~(KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK)) == 0);

        //Mac OS consumes keys Command+ these keys - the app will never see
        //them, so CTRL should not be remapped for these
        if (isOnlyMeta) {
            return (key != KeyEvent.VK_H) && (key != KeyEvent.VK_SPACE)
                    && (key != KeyEvent.VK_TAB);
        } else
            return !((key == KeyEvent.VK_D) && isMeta && isAlt);
    }
}

Related

  1. simulateEnterKey(Component c)
  2. simulateEnterKeyPressed(final Component component, final int delayInMilliseconds)
  3. simulateKeyStrokes(Component comp, String text)
  4. stringify(KeyStroke keyStroke)
  5. stringToKey(String s)
  6. translate(KeyStroke keyStroke, Locale locale)
  7. validarNumeroInteiro(KeyEvent evt)