source: josm/trunk/src/org/openstreetmap/josm/gui/util/AdvancedKeyPressDetector.java@ 12517

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

see #11924 - use extended event modifiers, deprecate old methods - see https://bugs.openjdk.java.net/browse/JDK-8143077

  • 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.util;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.AWTEvent;
7import java.awt.Component;
8import java.awt.KeyboardFocusManager;
9import java.awt.Toolkit;
10import java.awt.event.AWTEventListener;
11import java.awt.event.KeyEvent;
12import java.util.List;
13import java.util.Set;
14import java.util.TreeSet;
15import java.util.concurrent.CopyOnWriteArrayList;
16
17import javax.swing.JFrame;
18import javax.swing.SwingUtilities;
19import javax.swing.Timer;
20
21import org.openstreetmap.josm.Main;
22
23/**
24 * Helper object that allows cross-platform detection of key press and release events
25 * instance is available globally as {@code Main.map.keyDetector}.
26 * @since 7217
27 */
28public class AdvancedKeyPressDetector implements AWTEventListener {
29
30 // events for crossplatform key holding processing
31 // thanks to http://www.arco.in-berlin.de/keyevent.html
32 private final Set<Integer> set = new TreeSet<>();
33 private KeyEvent releaseEvent;
34 private Timer timer;
35
36 private final List<KeyPressReleaseListener> keyListeners = new CopyOnWriteArrayList<>();
37 @Deprecated
38 private final List<ModifierListener> modifierListeners = new CopyOnWriteArrayList<>();
39 private final List<ModifierExListener> modifierExListeners = new CopyOnWriteArrayList<>();
40 private int previousModifiers;
41 private int previousModifiersEx;
42
43 private boolean enabled = true;
44
45 /**
46 * Adds an object that wants to receive key press and release events.
47 * @param l listener to add
48 */
49 public void addKeyListener(KeyPressReleaseListener l) {
50 keyListeners.add(l);
51 }
52
53 /**
54 * Adds an object that wants to receive key modifier changed events.
55 * @param l listener to add
56 * @deprecated use {@link #addModifierExListener} instead
57 */
58 @Deprecated
59 public void addModifierListener(ModifierListener l) {
60 modifierListeners.add(l);
61 }
62
63 /**
64 * Adds an object that wants to receive extended key modifier changed events.
65 * @param l listener to add
66 * @since 12516
67 */
68 public void addModifierExListener(ModifierExListener l) {
69 modifierExListeners.add(l);
70 }
71
72 /**
73 * Removes the listener.
74 * @param l listener to remove
75 */
76 public void removeKeyListener(KeyPressReleaseListener l) {
77 keyListeners.remove(l);
78 }
79
80 /**
81 * Removes the key modifier listener.
82 * @param l listener to remove
83 * @deprecated use {@link #removeModifierExListener} instead
84 */
85 @Deprecated
86 public void removeModifierListener(ModifierListener l) {
87 modifierListeners.remove(l);
88 }
89
90 /**
91 * Removes the extended key modifier listener.
92 * @param l listener to remove
93 * @since 12516
94 */
95 public void removeModifierExListener(ModifierExListener l) {
96 modifierExListeners.remove(l);
97 }
98
99 /**
100 * Register this object as AWTEventListener
101 */
102 public void register() {
103 try {
104 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
105 } catch (SecurityException ex) {
106 Main.warn(ex);
107 }
108 timer = new Timer(0, e -> {
109 timer.stop();
110 if (set.remove(releaseEvent.getKeyCode()) && enabled && isFocusInMainWindow()) {
111 for (KeyPressReleaseListener q: keyListeners) {
112 q.doKeyReleased(releaseEvent);
113 }
114 }
115 });
116 }
117
118 /**
119 * Unregister this object as AWTEventListener
120 * lists of listeners are not cleared!
121 */
122 public void unregister() {
123 if (timer != null) {
124 timer.stop();
125 }
126 set.clear();
127 if (!keyListeners.isEmpty()) {
128 Main.warn(tr("Some of the key listeners forgot to remove themselves: {0}"), keyListeners.toString());
129 }
130 if (!modifierListeners.isEmpty()) {
131 Main.warn(tr("Some of the key modifier listeners forgot to remove themselves: {0}"), modifierListeners.toString());
132 }
133 if (!modifierExListeners.isEmpty()) {
134 Main.warn(tr("Some of the key modifier listeners forgot to remove themselves: {0}"), modifierExListeners.toString());
135 }
136 try {
137 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
138 } catch (SecurityException ex) {
139 Main.warn(ex);
140 }
141 }
142
143 private void processKeyEvent(KeyEvent e) {
144 if (Main.isTraceEnabled()) {
145 Main.trace("AdvancedKeyPressDetector enabled="+enabled+" => processKeyEvent("+e+") from "+new Exception().getStackTrace()[2]);
146 }
147 if (e.getID() == KeyEvent.KEY_PRESSED) {
148 if (timer.isRunning()) {
149 timer.stop();
150 } else if (set.add(e.getKeyCode()) && enabled && isFocusInMainWindow()) {
151 for (KeyPressReleaseListener q: keyListeners) {
152 if (Main.isTraceEnabled()) {
153 Main.trace(q+" => doKeyPressed("+e+')');
154 }
155 q.doKeyPressed(e);
156 }
157 }
158 } else if (e.getID() == KeyEvent.KEY_RELEASED) {
159 if (timer.isRunning()) {
160 timer.stop();
161 if (set.remove(e.getKeyCode()) && enabled && isFocusInMainWindow()) {
162 for (KeyPressReleaseListener q: keyListeners) {
163 if (Main.isTraceEnabled()) {
164 Main.trace(q+" => doKeyReleased("+e+')');
165 }
166 q.doKeyReleased(e);
167 }
168 }
169 } else {
170 releaseEvent = e;
171 timer.restart();
172 }
173 }
174 }
175
176 @Override
177 @SuppressWarnings("deprecation")
178 public void eventDispatched(AWTEvent e) {
179 if (!(e instanceof KeyEvent)) {
180 return;
181 }
182 KeyEvent ke = (KeyEvent) e;
183
184 // check if ctrl, alt, shift modifiers are changed
185 int modif = ke.getModifiers();
186 if (previousModifiers != modif) {
187 previousModifiers = modif;
188 for (ModifierListener m: modifierListeners) {
189 m.modifiersChanged(modif);
190 }
191 }
192
193 // check if ctrl, alt, shift extended modifiers are changed
194 int modifEx = ke.getModifiersEx();
195 if (previousModifiersEx != modifEx) {
196 previousModifiersEx = modifEx;
197 for (ModifierExListener m: modifierExListeners) {
198 m.modifiersExChanged(modifEx);
199 }
200 }
201
202 processKeyEvent(ke);
203 }
204
205 /**
206 * Allows to determine if the key with specific code is pressed now
207 * @param keyCode the key code, for example KeyEvent.VK_ENTER
208 * @return true if the key is pressed now
209 */
210 public boolean isKeyPressed(int keyCode) {
211 return set.contains(keyCode);
212 }
213
214 /**
215 * Sets the enabled state of the key detector. We need to disable it when text fields that disable
216 * shortcuts gain focus.
217 * @param enabled if {@code true}, enables this key detector. If {@code false}, disables it
218 * @since 7539
219 */
220 public final void setEnabled(boolean enabled) {
221 this.enabled = enabled;
222 if (Main.isTraceEnabled()) {
223 Main.trace("AdvancedKeyPressDetector enabled="+enabled+" from "+new Exception().getStackTrace()[1]);
224 }
225 }
226
227 private static boolean isFocusInMainWindow() {
228 Component focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
229 return focused != null && SwingUtilities.getWindowAncestor(focused) instanceof JFrame;
230 }
231}
Note: See TracBrowser for help on using the repository browser.