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

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

sonar - squid:S2325 - "private" methods that don't access instance data should be "static"

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