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

Last change on this file since 4999 was 4999, checked in by stoecker, 12 years ago

fix #7402 - multikey shortcut handling

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