source: josm/trunk/src/org/openstreetmap/josm/gui/util/MultikeyActionsHandler.java@ 12807

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

see #15229 - see #15182 - see #14794 - checkstyle, unit tests

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