source: josm/trunk/src/org/openstreetmap/josm/gui/widgets/DisableShortcutsOnFocusGainedTextField.java@ 17235

Last change on this file since 17235 was 17235, checked in by Don-vip, 3 years ago

fix #19120 - fix #19954 - Disable Shift shortcuts in text fields

  • Property svn:eol-style set to native
File size: 7.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.widgets;
3
4import java.awt.event.FocusEvent;
5import java.awt.event.InputEvent;
6import java.awt.event.KeyEvent;
7import java.util.ArrayList;
8import java.util.HashSet;
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;
16import javax.swing.text.Document;
17
18import org.openstreetmap.josm.actions.JosmAction;
19import org.openstreetmap.josm.gui.MainApplication;
20import org.openstreetmap.josm.tools.Pair;
21import org.openstreetmap.josm.tools.Shortcut;
22
23/**
24 * A JTextField that disabled all JOSM shortcuts composed of a single key without modifier (except F1 to F12),
25 * in order to avoid them to be triggered while typing.
26 * This allows to include text fields in toggle dialogs (needed for relation filter).
27 * @since 5696
28 */
29public class DisableShortcutsOnFocusGainedTextField extends JosmTextField {
30
31 /**
32 * Constructs a new <code>TextField</code>. A default model is created,
33 * the initial string is <code>null</code>, and the number of columns is set to 0.
34 */
35 public DisableShortcutsOnFocusGainedTextField() {
36 // Contents can be set with parent methods
37 }
38
39 /**
40 * Constructs a new <code>TextField</code> initialized with the
41 * specified text. A default model is created and the number of columns is 0.
42 *
43 * @param text the text to be displayed, or <code>null</code>
44 */
45 public DisableShortcutsOnFocusGainedTextField(String text) {
46 super(text);
47 }
48
49 /**
50 * Constructs a new empty <code>TextField</code> with the specified number of columns.
51 * A default model is created and the initial string is set to <code>null</code>.
52 *
53 * @param columns the number of columns to use to calculate
54 * the preferred width; if columns is set to zero, the
55 * preferred width will be whatever naturally results from the component implementation
56 */
57 public DisableShortcutsOnFocusGainedTextField(int columns) {
58 super(columns);
59 }
60
61 /**
62 * Constructs a new <code>TextField</code> initialized with the
63 * specified text and columns. A default model is created.
64 *
65 * @param text the text to be displayed, or <code>null</code>
66 * @param columns the number of columns to use to calculate
67 * the preferred width; if columns is set to zero, the
68 * preferred width will be whatever naturally results from the component implementation
69 */
70 public DisableShortcutsOnFocusGainedTextField(String text, int columns) {
71 super(text, columns);
72 }
73
74 /**
75 * Constructs a new <code>JTextField</code> that uses the given text
76 * storage model and the given number of columns.
77 * This is the constructor through which the other constructors feed.
78 * If the document is <code>null</code>, a default model is created.
79 *
80 * @param doc the text storage to use; if this is <code>null</code>,
81 * a default will be provided by calling the
82 * <code>createDefaultModel</code> method
83 * @param text the initial string to display, or <code>null</code>
84 * @param columns the number of columns to use to calculate
85 * the preferred width &gt;= 0; if <code>columns</code>
86 * is set to zero, the preferred width will be whatever
87 * naturally results from the component implementation
88 * @throws IllegalArgumentException if <code>columns</code> &lt; 0
89 */
90 public DisableShortcutsOnFocusGainedTextField(Document doc, String text, int columns) {
91 super(doc, text, columns);
92 }
93
94 private final transient List<Pair<Action, Shortcut>> unregisteredActionShortcuts = new ArrayList<>();
95 private final Set<JosmAction> disabledMenuActions = new HashSet<>();
96
97 @Override
98 public void focusGained(FocusEvent e) {
99 super.focusGained(e);
100 disableMenuActions();
101 unregisterActionShortcuts();
102 }
103
104 @Override
105 public void focusLost(FocusEvent e) {
106 super.focusLost(e);
107 restoreActionShortcuts();
108 restoreMenuActions();
109 }
110
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;
176 }
177
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();
186 }
187
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 }
197}
Note: See TracBrowser for help on using the repository browser.