source: josm/trunk/src/org/openstreetmap/josm/tools/MultikeyActionsHandler.java@ 11692

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

findbugs - SIC_INNER_SHOULD_BE_STATIC_ANON

  • Property svn:eol-style set to native
File size: 8.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.KeyEventDispatcher;
7import java.awt.KeyboardFocusManager;
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
10import java.util.HashMap;
11import java.util.Map;
12import java.util.Timer;
13import java.util.TimerTask;
14
15import javax.swing.AbstractAction;
16import javax.swing.Action;
17import javax.swing.JMenuItem;
18import javax.swing.JPanel;
19import javax.swing.JPopupMenu;
20import javax.swing.KeyStroke;
21import javax.swing.SwingUtilities;
22import javax.swing.event.PopupMenuEvent;
23import javax.swing.event.PopupMenuListener;
24
25import org.openstreetmap.josm.Main;
26import org.openstreetmap.josm.tools.MultikeyShortcutAction.MultikeyInfo;
27
28public final class MultikeyActionsHandler {
29
30 private static final long DIALOG_DELAY = 1000;
31 private static final String STATUS_BAR_ID = "multikeyShortcut";
32
33 private final Map<MultikeyShortcutAction, MyAction> myActions = new HashMap<>();
34
35 static final class ShowLayersPopupWorker implements Runnable {
36 static final class StatusLinePopupMenuListener implements PopupMenuListener {
37 @Override
38 public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
39 // Do nothing
40 }
41
42 @Override
43 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
44 Main.map.statusLine.resetHelpText(STATUS_BAR_ID);
45 }
46
47 @Override
48 public void popupMenuCanceled(PopupMenuEvent e) {
49 // Do nothing
50 }
51 }
52
53 private final MyAction action;
54
55 ShowLayersPopupWorker(MyAction action) {
56 this.action = action;
57 }
58
59 @Override
60 public void run() {
61 JPopupMenu layers = new JPopupMenu();
62
63 JMenuItem lbTitle = new JMenuItem((String) action.action.getValue(Action.SHORT_DESCRIPTION));
64 lbTitle.setEnabled(false);
65 JPanel pnTitle = new JPanel();
66 pnTitle.add(lbTitle);
67 layers.add(pnTitle);
68
69 char repeatKey = (char) action.shortcut.getKeyStroke().getKeyCode();
70 boolean repeatKeyUsed = false;
71
72 for (final MultikeyInfo info: action.action.getMultikeyCombinations()) {
73
74 if (info.getShortcut() == repeatKey) {
75 repeatKeyUsed = true;
76 }
77
78 JMenuItem item = new JMenuItem(formatMenuText(action.shortcut.getKeyStroke(),
79 String.valueOf(info.getShortcut()), info.getDescription()));
80 item.setMnemonic(info.getShortcut());
81 item.addActionListener(e -> action.action.executeMultikeyAction(info.getIndex(), false));
82 layers.add(item);
83 }
84
85 if (!repeatKeyUsed) {
86 MultikeyInfo lastLayer = action.action.getLastMultikeyAction();
87 if (lastLayer != null) {
88 JMenuItem repeateItem = new JMenuItem(formatMenuText(action.shortcut.getKeyStroke(),
89 KeyEvent.getKeyText(action.shortcut.getKeyStroke().getKeyCode()),
90 "Repeat " + lastLayer.getDescription()));
91 repeateItem.setMnemonic(action.shortcut.getKeyStroke().getKeyCode());
92 repeateItem.addActionListener(e -> action.action.executeMultikeyAction(-1, true));
93 layers.add(repeateItem);
94 }
95 }
96 layers.addPopupMenuListener(new StatusLinePopupMenuListener());
97 layers.show(Main.parent, Integer.MAX_VALUE, Integer.MAX_VALUE);
98 layers.setLocation(Main.parent.getX() + Main.parent.getWidth() - layers.getWidth(),
99 Main.parent.getY() + Main.parent.getHeight() - layers.getHeight());
100 }
101 }
102
103 private class MyKeyEventDispatcher implements KeyEventDispatcher {
104 @Override
105 public boolean dispatchKeyEvent(KeyEvent e) {
106
107 if (e.getWhen() == lastTimestamp)
108 return false;
109
110 if (lastAction != null && e.getID() == KeyEvent.KEY_PRESSED) {
111 int index = getIndex(e.getKeyCode());
112 if (index >= 0) {
113 lastAction.action.executeMultikeyAction(index, e.getKeyCode() == lastAction.shortcut.getKeyStroke().getKeyCode());
114 }
115 lastAction = null;
116 Main.map.statusLine.resetHelpText(STATUS_BAR_ID);
117 return true;
118 }
119 return false;
120 }
121
122 private int getIndex(int lastKey) {
123 if (lastKey >= KeyEvent.VK_1 && lastKey <= KeyEvent.VK_9)
124 return lastKey - KeyEvent.VK_1;
125 else if (lastKey == KeyEvent.VK_0)
126 return 9;
127 else if (lastKey >= KeyEvent.VK_A && lastKey <= KeyEvent.VK_Z)
128 return lastKey - KeyEvent.VK_A + 10;
129 else
130 return -1;
131 }
132 }
133
134 private class MyAction extends AbstractAction {
135
136 private final transient MultikeyShortcutAction action;
137 private final transient Shortcut shortcut;
138
139 MyAction(MultikeyShortcutAction action) {
140 this.action = action;
141 this.shortcut = action.getMultikeyShortcut();
142 }
143
144 @Override
145 public void actionPerformed(ActionEvent e) {
146 lastTimestamp = e.getWhen();
147 lastAction = this;
148 timer.schedule(new MyTimerTask(lastTimestamp, lastAction), DIALOG_DELAY);
149 Main.map.statusLine.setHelpText(STATUS_BAR_ID, tr("{0}... [please type its number]", (String) action.getValue(SHORT_DESCRIPTION)));
150 }
151
152 @Override
153 public String toString() {
154 return "MultikeyAction" + action;
155 }
156 }
157
158 private class MyTimerTask extends TimerTask {
159 private final long lastTimestamp;
160 private final MyAction lastAction;
161
162 MyTimerTask(long lastTimestamp, MyAction lastAction) {
163 this.lastTimestamp = lastTimestamp;
164 this.lastAction = lastAction;
165 }
166
167 @Override
168 public void run() {
169 if (lastTimestamp == MultikeyActionsHandler.this.lastTimestamp &&
170 lastAction == MultikeyActionsHandler.this.lastAction) {
171 SwingUtilities.invokeLater(new ShowLayersPopupWorker(lastAction));
172 MultikeyActionsHandler.this.lastAction = null;
173 }
174 }
175 }
176
177 private long lastTimestamp;
178 private MyAction lastAction;
179 private final Timer timer;
180
181 private MultikeyActionsHandler() {
182 KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new MyKeyEventDispatcher());
183 timer = new Timer();
184 }
185
186 private static MultikeyActionsHandler instance;
187
188 /**
189 * Replies the unique instance of this class.
190 * @return The unique instance of this class
191 */
192 public static synchronized MultikeyActionsHandler getInstance() {
193 if (instance == null) {
194 instance = new MultikeyActionsHandler();
195 }
196 return instance;
197 }
198
199 private static String formatMenuText(KeyStroke keyStroke, String index, String description) {
200 String shortcutText = KeyEvent.getKeyModifiersText(keyStroke.getModifiers()) + '+'
201 + KeyEvent.getKeyText(keyStroke.getKeyCode()) + ',' + index;
202
203 return "<html><i>" + shortcutText + "</i>&nbsp;&nbsp;&nbsp;&nbsp;" + description;
204 }
205
206 /**
207 * Registers an action and its shortcut
208 * @param action The action to add
209 */
210 public void addAction(MultikeyShortcutAction action) {
211 if (action.getMultikeyShortcut() != null) {
212 MyAction myAction = new MyAction(action);
213 myActions.put(action, myAction);
214 Main.registerActionShortcut(myAction, myAction.shortcut);
215 }
216 }
217
218 /**
219 * Unregisters an action and its shortcut completely
220 * @param action The action to remove
221 */
222 public void removeAction(MultikeyShortcutAction action) {
223 MyAction a = myActions.get(action);
224 if (a != null) {
225 Main.unregisterActionShortcut(a, a.shortcut);
226 myActions.remove(action);
227 }
228 }
229}
Note: See TracBrowser for help on using the repository browser.