Ticket #21443: 21443.patch

File 21443.patch, 13.3 KB (added by taylor.smock, 3 years ago)

Extract shortcut disablement to interface, with default implementations for focusGained/focusLost

  • new file src/org/openstreetmap/josm/gui/widgets/DisableShortcutsOnFocusGainedComponent.java

    diff --git a/src/org/openstreetmap/josm/gui/widgets/DisableShortcutsOnFocusGainedComponent.java b/src/org/openstreetmap/josm/gui/widgets/DisableShortcutsOnFocusGainedComponent.java
    new file mode 100644
    index 0000000000..a792c63749
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.widgets;
     3
     4import java.awt.Component;
     5import java.awt.event.FocusEvent;
     6import java.awt.event.FocusListener;
     7import java.awt.event.InputEvent;
     8import java.awt.event.KeyEvent;
     9import java.util.List;
     10import java.util.Set;
     11
     12import javax.swing.Action;
     13import javax.swing.JMenu;
     14import javax.swing.JMenuItem;
     15import javax.swing.KeyStroke;
     16
     17import org.openstreetmap.josm.actions.JosmAction;
     18import org.openstreetmap.josm.gui.MainApplication;
     19import org.openstreetmap.josm.tools.Pair;
     20import org.openstreetmap.josm.tools.Shortcut;
     21import org.openstreetmap.josm.tools.annotations.PrivateMethod;
     22
     23/**
     24 * An interface for components with code that can be used for disabling shortcuts while they hold focus.
     25 *
     26 * Warning: On migration to Java 9+, all methods marked
     27 * @author Taylor Smock
     28 * @since xxx (code extracted for {@link DisableShortcutsOnFocusGainedTextField}
     29 */
     30public interface DisableShortcutsOnFocusGainedComponent extends FocusListener {
     31
     32    @Override
     33    default void focusGained(FocusEvent e) {
     34        disableMenuActions();
     35        unregisterActionShortcuts();
     36    }
     37
     38    @Override
     39    default void focusLost(FocusEvent e) {
     40        restoreActionShortcuts();
     41        restoreMenuActions();
     42    }
     43
     44    /**
     45     * Get the unregistered action shortcuts.
     46     * This should not be used outside the {@link DisableShortcutsOnFocusGainedComponent} interface.
     47     * @return The list of unregistered action shortcuts (modifiable)
     48     */
     49    List<Pair<Action, Shortcut>> getUnregisteredActionShortcuts();
     50
     51    /**
     52     * Get the disabled menu action list
     53     * This should not be used outside the {@link DisableShortcutsOnFocusGainedComponent} interface.
     54     * @return The list of disabled menu actions (modifiable)
     55     */
     56    Set<JosmAction> getDisabledMenuActions();
     57
     58    /**
     59     * Disables all relevant menu actions.
     60     * @see #hasToBeDisabled
     61     */
     62    @PrivateMethod("Was protected")
     63    default void disableMenuActions() {
     64        getDisabledMenuActions().clear();
     65        for (int i = 0; i < MainApplication.getMenu().getMenuCount(); i++) {
     66            JMenu menu = MainApplication.getMenu().getMenu(i);
     67            if (menu != null) {
     68                for (int j = 0; j < menu.getItemCount(); j++) {
     69                    JMenuItem item = menu.getItem(j);
     70                    if (item != null) {
     71                        Action action = item.getAction();
     72                        if (action instanceof JosmAction && action.isEnabled()) {
     73                            Shortcut shortcut = ((JosmAction) action).getShortcut();
     74                            if (shortcut != null) {
     75                                KeyStroke ks = shortcut.getKeyStroke();
     76                                if (hasToBeDisabled(ks)) {
     77                                    action.setEnabled(false);
     78                                    getDisabledMenuActions().add((JosmAction) action);
     79                                }
     80                            }
     81                        }
     82                    }
     83                }
     84            }
     85        }
     86    }
     87
     88    /**
     89     * Unregisters all relevant action shortcuts.
     90     * @see #hasToBeDisabled
     91     */
     92    @PrivateMethod("Was protected")
     93    default void unregisterActionShortcuts() {
     94        getUnregisteredActionShortcuts().clear();
     95        // Unregister all actions with Shift modifier or without modifiers to avoid them to be triggered by typing in this text field
     96        for (Shortcut shortcut : Shortcut.listAll()) {
     97            KeyStroke ks = shortcut.getKeyStroke();
     98            if (hasToBeDisabled(ks)) {
     99                Action action = MainApplication.getRegisteredActionShortcut(shortcut);
     100                if (action != null) {
     101                    MainApplication.unregisterActionShortcut(action, shortcut);
     102                    getUnregisteredActionShortcuts().add(new Pair<>(action, shortcut));
     103                }
     104            }
     105        }
     106    }
     107
     108    /**
     109     * Returns true if the given shortcut has Shift modifier or no modifier and is not an actions key.
     110     * @param ks key stroke
     111     * @return {@code true} if the given shortcut has to be disabled
     112     * @see KeyEvent#isActionKey()
     113     */
     114    @PrivateMethod("Was protected")
     115    default boolean hasToBeDisabled(KeyStroke ks) {
     116        if (this instanceof Component) {
     117            return ks != null && (ks.getModifiers() == 0 || isOnlyShift(ks.getModifiers())) && !new KeyEvent((Component) this,
     118                    KeyEvent.KEY_PRESSED, 0, ks.getModifiers(), ks.getKeyCode(), ks.getKeyChar()).isActionKey();
     119        }
     120        throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not an instanceof Component");
     121    }
     122
     123    /**
     124     * Check if the modifiers is only shift
     125     * @param modifiers The modifiers to check
     126     * @return {@code true} if the only modifier is {@link InputEvent#SHIFT_DOWN_MASK}
     127     */
     128    @PrivateMethod("Was private")
     129    static boolean isOnlyShift(int modifiers) {
     130        return (modifiers & InputEvent.SHIFT_DOWN_MASK) != 0
     131                && (modifiers & InputEvent.CTRL_DOWN_MASK) == 0
     132                && (modifiers & InputEvent.ALT_DOWN_MASK) == 0
     133                && (modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) == 0
     134                && (modifiers & InputEvent.META_DOWN_MASK) == 0;
     135    }
     136
     137    /**
     138     * Restore all actions previously disabled
     139     */
     140    @PrivateMethod("Was protected")
     141    default void restoreMenuActions() {
     142        for (JosmAction a : getDisabledMenuActions()) {
     143            a.setEnabled(true);
     144        }
     145        getDisabledMenuActions().clear();
     146    }
     147
     148    /**
     149     * Restore all action shortcuts previously unregistered
     150     */
     151    @PrivateMethod("Was protected")
     152    default void restoreActionShortcuts() {
     153        for (Pair<Action, Shortcut> p : getUnregisteredActionShortcuts()) {
     154            MainApplication.registerActionShortcut(p.a, p.b);
     155        }
     156        getUnregisteredActionShortcuts().clear();
     157    }
     158}
  • src/org/openstreetmap/josm/gui/widgets/DisableShortcutsOnFocusGainedTextField.java

    diff --git a/src/org/openstreetmap/josm/gui/widgets/DisableShortcutsOnFocusGainedTextField.java b/src/org/openstreetmap/josm/gui/widgets/DisableShortcutsOnFocusGainedTextField.java
    index 13e8e687f8..b035615d2c 100644
    a b  
    22package org.openstreetmap.josm.gui.widgets;
    33
    44import java.awt.event.FocusEvent;
    5 import java.awt.event.InputEvent;
    6 import java.awt.event.KeyEvent;
    75import java.util.ArrayList;
    86import java.util.HashSet;
    97import java.util.List;
    108import java.util.Set;
    119
    1210import javax.swing.Action;
    13 import javax.swing.JMenu;
    14 import javax.swing.JMenuItem;
    15 import javax.swing.KeyStroke;
    1611import javax.swing.text.Document;
    1712
    1813import org.openstreetmap.josm.actions.JosmAction;
    19 import org.openstreetmap.josm.gui.MainApplication;
    2014import org.openstreetmap.josm.tools.Pair;
    2115import org.openstreetmap.josm.tools.Shortcut;
    2216
    import org.openstreetmap.josm.tools.Shortcut;  
    2620 * This allows to include text fields in toggle dialogs (needed for relation filter).
    2721 * @since 5696
    2822 */
    29 public class DisableShortcutsOnFocusGainedTextField extends JosmTextField {
     23public class DisableShortcutsOnFocusGainedTextField extends JosmTextField implements DisableShortcutsOnFocusGainedComponent {
    3024
    3125    /**
    3226     * Constructs a new <code>TextField</code>. A default model is created,
    public class DisableShortcutsOnFocusGainedTextField extends JosmTextField {  
    9791    @Override
    9892    public void focusGained(FocusEvent e) {
    9993        super.focusGained(e);
    100         disableMenuActions();
    101         unregisterActionShortcuts();
     94        DisableShortcutsOnFocusGainedComponent.super.focusGained(e);
    10295    }
    10396
    10497    @Override
    10598    public void focusLost(FocusEvent e) {
    10699        super.focusLost(e);
    107         restoreActionShortcuts();
    108         restoreMenuActions();
     100        DisableShortcutsOnFocusGainedComponent.super.focusLost(e);
    109101    }
    110102
    111     /**
    112      * Disables all relevant menu actions.
    113      * @see #hasToBeDisabled
    114      */
    115     protected void disableMenuActions() {
    116         disabledMenuActions.clear();
    117         for (int i = 0; i < MainApplication.getMenu().getMenuCount(); i++) {
    118             JMenu menu = MainApplication.getMenu().getMenu(i);
    119             if (menu != null) {
    120                 for (int j = 0; j < menu.getItemCount(); j++) {
    121                     JMenuItem item = menu.getItem(j);
    122                     if (item != null) {
    123                         Action action = item.getAction();
    124                         if (action instanceof JosmAction && action.isEnabled()) {
    125                             Shortcut shortcut = ((JosmAction) action).getShortcut();
    126                             if (shortcut != null) {
    127                                 KeyStroke ks = shortcut.getKeyStroke();
    128                                 if (hasToBeDisabled(ks)) {
    129                                     action.setEnabled(false);
    130                                     disabledMenuActions.add((JosmAction) action);
    131                                 }
    132                             }
    133                         }
    134                     }
    135                 }
    136             }
    137         }
    138     }
    139 
    140     /**
    141      * Unregisters all relevant action shortcuts.
    142      * @see #hasToBeDisabled
    143      */
    144     protected void unregisterActionShortcuts() {
    145         unregisteredActionShortcuts.clear();
    146         // Unregister all actions with Shift modifier or without modifiers to avoid them to be triggered by typing in this text field
    147         for (Shortcut shortcut : Shortcut.listAll()) {
    148             KeyStroke ks = shortcut.getKeyStroke();
    149             if (hasToBeDisabled(ks)) {
    150                 Action action = MainApplication.getRegisteredActionShortcut(shortcut);
    151                 if (action != null) {
    152                     MainApplication.unregisterActionShortcut(action, shortcut);
    153                     unregisteredActionShortcuts.add(new Pair<>(action, shortcut));
    154                 }
    155             }
    156         }
    157     }
    158 
    159     /**
    160      * Returns true if the given shortcut has Shift modifier or no modifier and is not an actions key.
    161      * @param ks key stroke
    162      * @return {@code true} if the given shortcut has to be disabled
    163      * @see KeyEvent#isActionKey()
    164      */
    165     protected boolean hasToBeDisabled(KeyStroke ks) {
    166         return ks != null && (ks.getModifiers() == 0 || isOnlyShift(ks.getModifiers())) && !new KeyEvent(
    167                 this, KeyEvent.KEY_PRESSED, 0, ks.getModifiers(), ks.getKeyCode(), ks.getKeyChar()).isActionKey();
    168     }
    169 
    170     private static boolean isOnlyShift(int modifiers) {
    171         return (modifiers & InputEvent.SHIFT_DOWN_MASK) != 0
    172                 && (modifiers & InputEvent.CTRL_DOWN_MASK) == 0
    173                 && (modifiers & InputEvent.ALT_DOWN_MASK) == 0
    174                 && (modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) == 0
    175                 && (modifiers & InputEvent.META_DOWN_MASK) == 0;
     103    @Override
     104    public List<Pair<Action, Shortcut>> getUnregisteredActionShortcuts() {
     105        return this.unregisteredActionShortcuts;
    176106    }
    177107
    178     /**
    179      * Restore all actions previously disabled
    180      */
    181     protected void restoreMenuActions() {
    182         for (JosmAction a : disabledMenuActions) {
    183             a.setEnabled(true);
    184         }
    185         disabledMenuActions.clear();
     108    @Override
     109    public Set<JosmAction> getDisabledMenuActions() {
     110        return this.disabledMenuActions;
    186111    }
    187112
    188     /**
    189      * Restore all action shortcuts previously unregistered
    190      */
    191     protected void restoreActionShortcuts() {
    192         for (Pair<Action, Shortcut> p : unregisteredActionShortcuts) {
    193             MainApplication.registerActionShortcut(p.a, p.b);
    194         }
    195         unregisteredActionShortcuts.clear();
    196     }
    197113}
  • new file src/org/openstreetmap/josm/tools/annotations/PrivateMethod.java

    diff --git a/src/org/openstreetmap/josm/tools/annotations/PrivateMethod.java b/src/org/openstreetmap/josm/tools/annotations/PrivateMethod.java
    new file mode 100644
    index 0000000000..7e7e4554ba
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.tools.annotations;
     3
     4/**
     5 * This annotation marks methods that will become private when JOSM migrates the codebase to JOSM 9+.
     6 * This is typically used in interfaces.
     7 * @author Taylor Smock
     8 * @since xxx
     9 */
     10public @interface PrivateMethod {
     11    /**
     12     * The reason why this method will become private
     13     * @return The reason
     14     */
     15    String value() default "";
     16}
  • new file src/org/openstreetmap/josm/tools/annotations/package-info.java

    diff --git a/src/org/openstreetmap/josm/tools/annotations/package-info.java b/src/org/openstreetmap/josm/tools/annotations/package-info.java
    new file mode 100644
    index 0000000000..eeb988cf11
    - +  
     1// License: GPL. For details, see LICENSE file.
     2
     3/**
     4 * Provides the annotations to mark items for future changes
     5 */
     6package org.openstreetmap.josm.tools.annotations;