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

Last change on this file since 7937 was 7937, checked in by bastiK, 9 years ago

add subversion property svn:eol=native

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