001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.tools;
003
004import java.awt.event.InputEvent;
005import java.awt.event.KeyEvent;
006import java.util.Optional;
007
008import javax.swing.Action;
009import javax.swing.InputMap;
010import javax.swing.JButton;
011import javax.swing.JComponent;
012import javax.swing.KeyStroke;
013import javax.swing.SwingUtilities;
014
015/**
016 * Tools to work with Swing InputMap.
017 * @since 5200
018 */
019public final class InputMapUtils {
020
021    private InputMapUtils() {
022        // Hide default constructor for utils classes
023    }
024
025    /**
026     * Unassign Ctrl-Shift/Alt-Shift Up/Down from the given component
027     * to allow global JOSM shortcuts to work in this component.
028     * @param cmp The Swing component
029     * @param condition one of the following values:
030     * <ul>
031     * <li>JComponent.FOCUS_INPUTMAP_CREATED
032     * <li>JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
033     * <li>JComponent.WHEN_IN_FOCUSED_WINDOW
034     * </ul>
035     */
036    public static void unassignCtrlShiftUpDown(JComponent cmp, int condition) {
037        InputMap inputMap = SwingUtilities.getUIInputMap(cmp, condition);
038        inputMap.remove(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
039        inputMap.remove(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
040        inputMap.remove(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
041        inputMap.remove(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
042        SwingUtilities.replaceUIInputMap(cmp, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
043    }
044
045    /**
046     * Unassign PageUp/PageDown from the given component
047     * to allow global JOSM shortcuts to work in this component.
048     * @param cmp The Swing component
049     * @param condition one of the following values:
050     * <ul>
051     * <li>JComponent.FOCUS_INPUTMAP_CREATED
052     * <li>JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
053     * <li>JComponent.WHEN_IN_FOCUSED_WINDOW
054     * </ul>
055     * @since 6557
056     */
057    public static void unassignPageUpDown(JComponent cmp, int condition) {
058        InputMap inputMap = SwingUtilities.getUIInputMap(cmp, condition);
059        inputMap.remove(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0));
060        inputMap.remove(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0));
061        SwingUtilities.replaceUIInputMap(cmp, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
062    }
063
064    /**
065     * Enable activating button on Enter (which is replaced with spacebar for certain Look-And-Feels).
066     * @param b Button
067     */
068    public static void enableEnter(JButton b) {
069         b.setFocusable(true);
070         addEnterAction(b, b.getAction());
071    }
072
073    /**
074     * Add an action activated with Enter key on a component.
075     * @param c The Swing component
076     * @param a action activated with Enter key
077     * @see JComponent#WHEN_FOCUSED
078     */
079    public static void addEnterAction(JComponent c, Action a) {
080        addEnterAction(c, a, JComponent.WHEN_FOCUSED);
081    }
082
083    /**
084     * Add an action activated with Enter key on a component or its children.
085     * @param c The Swing component
086     * @param a action activated with Enter key
087     * @see JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
088     * @since 10790
089     */
090    public static void addEnterActionWhenAncestor(JComponent c, Action a) {
091         addEnterAction(c, a, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
092    }
093
094    private static void addEnterAction(JComponent c, Action a, int condition) {
095         c.getActionMap().put("enter", a);
096         c.getInputMap(condition).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enter");
097    }
098
099    /**
100     * Add an action activated with Spacebar key on a component.
101     * @param c The Swing component
102     * @param a action activated with Spacebar key
103     */
104    public static void addSpacebarAction(JComponent c, Action a) {
105         c.getActionMap().put("spacebar", a);
106         c.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "spacebar");
107    }
108
109    /**
110     * Add an action activated with ESCAPE key on a component or its children.
111     * @param c The Swing component
112     * @param a action activated with ESCAPE key
113     * @see JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
114     * @since 10791
115     */
116    public static void addEscapeAction(JComponent c, Action a) {
117         c.getActionMap().put("escape", a);
118         c.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "escape");
119    }
120
121    /**
122     * Add an action activated with Ctrl+Enter key on a component.
123     * @param c The Swing component
124     * @param a action activated with Ctrl+Enter key
125     * @see JComponent#WHEN_IN_FOCUSED_WINDOW
126     */
127    public static void addCtrlEnterAction(JComponent c, Action a) {
128        final KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK);
129        c.getActionMap().put("ctrl_enter", a);
130        c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(stroke, "ctrl_enter");
131        Optional.ofNullable(a)
132                .map(x -> x.getValue(Action.SHORT_DESCRIPTION))
133                .map(String::valueOf)
134                .ifPresent(text -> Shortcut.setTooltip(a, text, stroke));
135    }
136}