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

Last change on this file since 5463 was 5463, checked in by jttt, 12 years ago

Fix some memory leaks (see #7980)

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