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

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

sonar - squid:S1066 - Collapsible "if" statements should be merged

  • Property svn:eol-style set to native
File size: 6.2 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 private final List<ModifierListener> modifierListeners = new CopyOnWriteArrayList<>();
38 private int previousModifiers;
39
40 private boolean enabled = true;
41
42 /**
43 * Adds an object that wants to receive key press and release events.
44 * @param l listener to add
45 */
46 public void addKeyListener(KeyPressReleaseListener l) {
47 keyListeners.add(l);
48 }
49
50 /**
51 * Adds an object that wants to receive key modifier changed events.
52 * @param l listener to add
53 */
54 public void addModifierListener(ModifierListener l) {
55 modifierListeners.add(l);
56 }
57
58 /**
59 * Removes the listener.
60 * @param l listener to remove
61 */
62 public void removeKeyListener(KeyPressReleaseListener l) {
63 keyListeners.remove(l);
64 }
65
66 /**
67 * Removes the key modifier listener.
68 * @param l listener to remove
69 */
70 public void removeModifierListener(ModifierListener l) {
71 modifierListeners.remove(l);
72 }
73
74 /**
75 * Register this object as AWTEventListener
76 */
77 public void register() {
78 try {
79 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
80 } catch (SecurityException ex) {
81 Main.warn(ex);
82 }
83 timer = new Timer(0, e -> {
84 timer.stop();
85 if (set.remove(releaseEvent.getKeyCode()) && enabled && isFocusInMainWindow()) {
86 for (KeyPressReleaseListener q: keyListeners) {
87 q.doKeyReleased(releaseEvent);
88 }
89 }
90 });
91 }
92
93 /**
94 * Unregister this object as AWTEventListener
95 * lists of listeners are not cleared!
96 */
97 public void unregister() {
98 if (timer != null) {
99 timer.stop();
100 }
101 set.clear();
102 if (!keyListeners.isEmpty()) {
103 Main.warn(tr("Some of the key listeners forgot to remove themselves: {0}"), keyListeners.toString());
104 }
105 if (!modifierListeners.isEmpty()) {
106 Main.warn(tr("Some of the key modifier listeners forgot to remove themselves: {0}"), modifierListeners.toString());
107 }
108 try {
109 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
110 } catch (SecurityException ex) {
111 Main.warn(ex);
112 }
113 }
114
115 private void processKeyEvent(KeyEvent e) {
116 if (Main.isTraceEnabled()) {
117 Main.trace("AdvancedKeyPressDetector enabled="+enabled+" => processKeyEvent("+e+") from "+new Exception().getStackTrace()[2]);
118 }
119 if (e.getID() == KeyEvent.KEY_PRESSED) {
120 if (timer.isRunning()) {
121 timer.stop();
122 } else if (set.add(e.getKeyCode()) && enabled && isFocusInMainWindow()) {
123 for (KeyPressReleaseListener q: keyListeners) {
124 if (Main.isTraceEnabled()) {
125 Main.trace(q+" => doKeyPressed("+e+')');
126 }
127 q.doKeyPressed(e);
128 }
129 }
130 } else if (e.getID() == KeyEvent.KEY_RELEASED) {
131 if (timer.isRunning()) {
132 timer.stop();
133 if (set.remove(e.getKeyCode()) && enabled && isFocusInMainWindow()) {
134 for (KeyPressReleaseListener q: keyListeners) {
135 if (Main.isTraceEnabled()) {
136 Main.trace(q+" => doKeyReleased("+e+')');
137 }
138 q.doKeyReleased(e);
139 }
140 }
141 } else {
142 releaseEvent = e;
143 timer.restart();
144 }
145 }
146 }
147
148 @Override
149 public void eventDispatched(AWTEvent e) {
150 if (!(e instanceof KeyEvent)) {
151 return;
152 }
153 KeyEvent ke = (KeyEvent) e;
154
155 // check if ctrl, alt, shift modifiers are changed
156 int modif = ke.getModifiers();
157 if (previousModifiers != modif) {
158 previousModifiers = modif;
159 for (ModifierListener m: modifierListeners) {
160 m.modifiersChanged(modif);
161 }
162 }
163
164 processKeyEvent(ke);
165 }
166
167 /**
168 * Allows to determine if the key with specific code is pressed now
169 * @param keyCode the key code, for example KeyEvent.VK_ENTER
170 * @return true if the key is pressed now
171 */
172 public boolean isKeyPressed(int keyCode) {
173 return set.contains(keyCode);
174 }
175
176 /**
177 * Sets the enabled state of the key detector. We need to disable it when text fields that disable
178 * shortcuts gain focus.
179 * @param enabled if {@code true}, enables this key detector. If {@code false}, disables it
180 * @since 7539
181 */
182 public final void setEnabled(boolean enabled) {
183 this.enabled = enabled;
184 if (Main.isTraceEnabled()) {
185 Main.trace("AdvancedKeyPressDetector enabled="+enabled+" from "+new Exception().getStackTrace()[1]);
186 }
187 }
188
189 private static boolean isFocusInMainWindow() {
190 Component focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
191 return focused != null && SwingUtilities.getWindowAncestor(focused) instanceof JFrame;
192 }
193}
Note: See TracBrowser for help on using the repository browser.