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

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

see #15182 - deprecate shortcut handling and mapframe listener methods in Main. Replacement: same methods in gui.MainApplication

  • Property svn:eol-style set to native
File size: 8.2 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.gui.MainApplication;
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 static final class ShowLayersPopupWorker implements Runnable {
37 static final class StatusLinePopupMenuListener implements PopupMenuListener {
38 @Override
39 public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
40 // Do nothing
41 }
42
43 @Override
44 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
45 MainApplication.getMap().statusLine.resetHelpText(STATUS_BAR_ID);
46 }
47
48 @Override
49 public void popupMenuCanceled(PopupMenuEvent e) {
50 // Do nothing
51 }
52 }
53
54 private final MyAction action;
55
56 ShowLayersPopupWorker(MyAction action) {
57 this.action = action;
58 }
59
60 @Override
61 public void run() {
62 JPopupMenu layers = new JPopupMenu();
63
64 JMenuItem lbTitle = new JMenuItem((String) action.action.getValue(Action.SHORT_DESCRIPTION));
65 lbTitle.setEnabled(false);
66 JPanel pnTitle = new JPanel();
67 pnTitle.add(lbTitle);
68 layers.add(pnTitle);
69
70 char repeatKey = (char) action.shortcut.getKeyStroke().getKeyCode();
71 boolean repeatKeyUsed = false;
72
73 for (final MultikeyInfo info: action.action.getMultikeyCombinations()) {
74
75 if (info.getShortcut() == repeatKey) {
76 repeatKeyUsed = true;
77 }
78
79 JMenuItem item = new JMenuItem(formatMenuText(action.shortcut.getKeyStroke(),
80 String.valueOf(info.getShortcut()), info.getDescription()));
81 item.setMnemonic(info.getShortcut());
82 item.addActionListener(e -> action.action.executeMultikeyAction(info.getIndex(), false));
83 layers.add(item);
84 }
85
86 if (!repeatKeyUsed) {
87 MultikeyInfo lastLayer = action.action.getLastMultikeyAction();
88 if (lastLayer != null) {
89 JMenuItem repeateItem = new JMenuItem(formatMenuText(action.shortcut.getKeyStroke(),
90 KeyEvent.getKeyText(action.shortcut.getKeyStroke().getKeyCode()),
91 "Repeat " + lastLayer.getDescription()));
92 repeateItem.setMnemonic(action.shortcut.getKeyStroke().getKeyCode());
93 repeateItem.addActionListener(e -> action.action.executeMultikeyAction(-1, true));
94 layers.add(repeateItem);
95 }
96 }
97 layers.addPopupMenuListener(new StatusLinePopupMenuListener());
98 layers.show(Main.parent, Integer.MAX_VALUE, Integer.MAX_VALUE);
99 layers.setLocation(Main.parent.getX() + Main.parent.getWidth() - layers.getWidth(),
100 Main.parent.getY() + Main.parent.getHeight() - layers.getHeight());
101 }
102 }
103
104 private class MyKeyEventDispatcher implements KeyEventDispatcher {
105 @Override
106 public boolean dispatchKeyEvent(KeyEvent e) {
107
108 if (e.getWhen() == lastTimestamp)
109 return false;
110
111 if (lastAction != null && e.getID() == KeyEvent.KEY_PRESSED) {
112 int index = getIndex(e.getKeyCode());
113 if (index >= 0) {
114 lastAction.action.executeMultikeyAction(index, e.getKeyCode() == lastAction.shortcut.getKeyStroke().getKeyCode());
115 }
116 lastAction = null;
117 MainApplication.getMap().statusLine.resetHelpText(STATUS_BAR_ID);
118 return true;
119 }
120 return false;
121 }
122
123 private int getIndex(int lastKey) {
124 if (lastKey >= KeyEvent.VK_1 && lastKey <= KeyEvent.VK_9)
125 return lastKey - KeyEvent.VK_1;
126 else if (lastKey == KeyEvent.VK_0)
127 return 9;
128 else if (lastKey >= KeyEvent.VK_A && lastKey <= KeyEvent.VK_Z)
129 return lastKey - KeyEvent.VK_A + 10;
130 else
131 return -1;
132 }
133 }
134
135 private class MyAction extends AbstractAction {
136
137 private final transient MultikeyShortcutAction action;
138 private final transient Shortcut shortcut;
139
140 MyAction(MultikeyShortcutAction action) {
141 this.action = action;
142 this.shortcut = action.getMultikeyShortcut();
143 }
144
145 @Override
146 public void actionPerformed(ActionEvent e) {
147 lastTimestamp = e.getWhen();
148 lastAction = this;
149 timer.schedule(new MyTimerTask(lastTimestamp, lastAction), DIALOG_DELAY);
150 MainApplication.getMap().statusLine.setHelpText(STATUS_BAR_ID, tr("{0}... [please type its number]", (String) action.getValue(SHORT_DESCRIPTION)));
151 }
152
153 @Override
154 public String toString() {
155 return "MultikeyAction" + action;
156 }
157 }
158
159 private class MyTimerTask extends TimerTask {
160 private final long lastTimestamp;
161 private final MyAction lastAction;
162
163 MyTimerTask(long lastTimestamp, MyAction lastAction) {
164 this.lastTimestamp = lastTimestamp;
165 this.lastAction = lastAction;
166 }
167
168 @Override
169 public void run() {
170 if (lastTimestamp == MultikeyActionsHandler.this.lastTimestamp &&
171 lastAction == MultikeyActionsHandler.this.lastAction) {
172 SwingUtilities.invokeLater(new ShowLayersPopupWorker(lastAction));
173 MultikeyActionsHandler.this.lastAction = null;
174 }
175 }
176 }
177
178 private long lastTimestamp;
179 private MyAction lastAction;
180 private final Timer timer;
181
182 private MultikeyActionsHandler() {
183 KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new MyKeyEventDispatcher());
184 timer = new Timer();
185 }
186
187 private static MultikeyActionsHandler instance;
188
189 /**
190 * Replies the unique instance of this class.
191 * @return The unique instance of this class
192 */
193 public static synchronized MultikeyActionsHandler getInstance() {
194 if (instance == null) {
195 instance = new MultikeyActionsHandler();
196 }
197 return instance;
198 }
199
200 private static String formatMenuText(KeyStroke keyStroke, String index, String description) {
201 String shortcutText = Shortcut.getKeyText(keyStroke) + ',' + 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 MainApplication.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 MainApplication.unregisterActionShortcut(a, a.shortcut);
226 myActions.remove(action);
227 }
228 }
229}
Note: See TracBrowser for help on using the repository browser.