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

Last change on this file since 9704 was 9634, checked in by Don-vip, 8 years ago

fix findbugs/coverity issues

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