| | 1 | // License: GPL. For details, see LICENSE file. |
| | 2 | package org.openstreetmap.josm.gui.widgets; |
| | 3 | |
| | 4 | import java.awt.Component; |
| | 5 | import java.awt.event.FocusEvent; |
| | 6 | import java.awt.event.FocusListener; |
| | 7 | import java.awt.event.InputEvent; |
| | 8 | import java.awt.event.KeyEvent; |
| | 9 | import java.util.List; |
| | 10 | import java.util.Set; |
| | 11 | |
| | 12 | import javax.swing.Action; |
| | 13 | import javax.swing.JMenu; |
| | 14 | import javax.swing.JMenuItem; |
| | 15 | import javax.swing.KeyStroke; |
| | 16 | |
| | 17 | import org.openstreetmap.josm.actions.JosmAction; |
| | 18 | import org.openstreetmap.josm.gui.MainApplication; |
| | 19 | import org.openstreetmap.josm.tools.Pair; |
| | 20 | import org.openstreetmap.josm.tools.Shortcut; |
| | 21 | import 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 | */ |
| | 30 | public 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 | * Note: This was protected |
| | 61 | * @see #hasToBeDisabled |
| | 62 | */ |
| | 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 | * Note: This was protected |
| | 91 | * @see #hasToBeDisabled |
| | 92 | */ |
| | 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 | * Note: This was protected |
| | 111 | * @param ks key stroke |
| | 112 | * @return {@code true} if the given shortcut has to be disabled |
| | 113 | * @see KeyEvent#isActionKey() |
| | 114 | */ |
| | 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 | * Note: This was private |
| | 126 | * @param modifiers The modifiers to check |
| | 127 | * @return {@code true} if the only modifier is {@link InputEvent#SHIFT_DOWN_MASK} |
| | 128 | */ |
| | 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 | * Note: This was protected |
| | 140 | */ |
| | 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 | * Note: This was protected |
| | 151 | */ |
| | 152 | default void restoreActionShortcuts() { |
| | 153 | for (Pair<Action, Shortcut> p : getUnregisteredActionShortcuts()) { |
| | 154 | MainApplication.registerActionShortcut(p.a, p.b); |
| | 155 | } |
| | 156 | getUnregisteredActionShortcuts().clear(); |
| | 157 | } |
| | 158 | } |