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

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

see #15182 - deprecate all Main logging methods and introduce suitable replacements in Logging for most of them

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