source: josm/trunk/src/org/openstreetmap/josm/tools/Shortcut.java@ 7253

Last change on this file since 7253 was 7012, checked in by Don-vip, 10 years ago

see #8465 - use String switch/case where applicable

  • Property svn:eol-style set to native
File size: 17.4 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.event.KeyEvent;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.HashMap;
10import java.util.LinkedHashMap;
11import java.util.LinkedList;
12import java.util.List;
13import java.util.Map;
14
15import javax.swing.AbstractAction;
16import javax.swing.AbstractButton;
17import javax.swing.JMenu;
18import javax.swing.KeyStroke;
19
20import org.openstreetmap.josm.Main;
21
22/**
23 * Global shortcut class.
24 *
25 * Note: This class represents a single shortcut, contains the factory to obtain
26 * shortcut objects from, manages shortcuts and shortcut collisions, and
27 * finally manages loading and saving shortcuts to/from the preferences.
28 *
29 * Action authors: You only need the {@link #registerShortcut} factory. Ignore everything
30 * else.
31 *
32 * All: Use only public methods that are also marked to be used. The others are
33 * public so the shortcut preferences can use them.
34 *
35 */
36public final class Shortcut {
37 private String shortText; // the unique ID of the shortcut
38 private String longText; // a human readable description that will be shown in the preferences
39 private final int requestedKey; // the key, the caller requested
40 private final int requestedGroup;// the group, the caller requested
41 private int assignedKey; // the key that actually is used
42 private int assignedModifier; // the modifiers that are used
43 private boolean assignedDefault; // true if it got assigned what was requested. (Note: modifiers will be ignored in favour of group when loading it from the preferences then.)
44 private boolean assignedUser; // true if the user changed this shortcut
45 private boolean automatic; // true if the user cannot change this shortcut (Note: it also will not be saved into the preferences)
46 private boolean reset; // true if the user requested this shortcut to be set to its default value (will happen on next restart, as this shortcut will not be saved to the preferences)
47
48 // simple constructor
49 private Shortcut(String shortText, String longText, int requestedKey, int requestedGroup, int assignedKey, int assignedModifier, boolean assignedDefault, boolean assignedUser) {
50 this.shortText = shortText;
51 this.longText = longText;
52 this.requestedKey = requestedKey;
53 this.requestedGroup = requestedGroup;
54 this.assignedKey = assignedKey;
55 this.assignedModifier = assignedModifier;
56 this.assignedDefault = assignedDefault;
57 this.assignedUser = assignedUser;
58 this.automatic = false;
59 this.reset = false;
60 }
61
62 public String getShortText() {
63 return shortText;
64 }
65
66 public String getLongText() {
67 return longText;
68 }
69
70 // a shortcut will be renamed when it is handed out again, because the original name
71 // may be a dummy
72 private void setLongText(String longText) {
73 this.longText = longText;
74 }
75
76 public int getAssignedKey() {
77 return assignedKey;
78 }
79
80 public int getAssignedModifier() {
81 return assignedModifier;
82 }
83
84 public boolean getAssignedDefault() {
85 return assignedDefault;
86 }
87
88 public boolean getAssignedUser() {
89 return assignedUser;
90 }
91
92 public boolean getAutomatic() {
93 return automatic;
94 }
95
96 public boolean isChangeable() {
97 return !automatic && !"core:none".equals(shortText);
98 }
99
100 private boolean getReset() {
101 return reset;
102 }
103
104 /**
105 * FOR PREF PANE ONLY
106 */
107 public void setAutomatic() {
108 automatic = true;
109 }
110
111 /**
112 * FOR PREF PANE ONLY
113 */
114 public void setAssignedModifier(int assignedModifier) {
115 this.assignedModifier = assignedModifier;
116 }
117
118 /**
119 * FOR PREF PANE ONLY
120 */
121 public void setAssignedKey(int assignedKey) {
122 this.assignedKey = assignedKey;
123 }
124
125 /**
126 * FOR PREF PANE ONLY
127 */
128 public void setAssignedUser(boolean assignedUser) {
129 this.reset = (this.assignedUser || reset) && !assignedUser;
130 if (assignedUser) {
131 assignedDefault = false;
132 } else if (reset) {
133 assignedKey = requestedKey;
134 assignedModifier = findModifier(requestedGroup, null);
135 }
136 this.assignedUser = assignedUser;
137 }
138
139 /**
140 * Use this to register the shortcut with Swing
141 */
142 public KeyStroke getKeyStroke() {
143 if (assignedModifier != -1)
144 return KeyStroke.getKeyStroke(assignedKey, assignedModifier);
145 return null;
146 }
147
148 // create a shortcut object from an string as saved in the preferences
149 private Shortcut(String prefString) {
150 List<String> s = (new ArrayList<>(Main.pref.getCollection(prefString)));
151 this.shortText = prefString.substring(15);
152 this.longText = s.get(0);
153 this.requestedKey = Integer.parseInt(s.get(1));
154 this.requestedGroup = Integer.parseInt(s.get(2));
155 this.assignedKey = Integer.parseInt(s.get(3));
156 this.assignedModifier = Integer.parseInt(s.get(4));
157 this.assignedDefault = Boolean.parseBoolean(s.get(5));
158 this.assignedUser = Boolean.parseBoolean(s.get(6));
159 }
160
161 private void saveDefault() {
162 Main.pref.getCollection("shortcut.entry."+shortText, Arrays.asList(new String[]{longText,
163 String.valueOf(requestedKey), String.valueOf(requestedGroup), String.valueOf(requestedKey),
164 String.valueOf(getGroupModifier(requestedGroup)), String.valueOf(true), String.valueOf(false)}));
165 }
166
167 // get a string that can be put into the preferences
168 private boolean save() {
169 if (getAutomatic() || getReset() || !getAssignedUser()) {
170 return Main.pref.putCollection("shortcut.entry."+shortText, null);
171 } else {
172 return Main.pref.putCollection("shortcut.entry."+shortText, Arrays.asList(new String[]{longText,
173 String.valueOf(requestedKey), String.valueOf(requestedGroup), String.valueOf(assignedKey),
174 String.valueOf(assignedModifier), String.valueOf(assignedDefault), String.valueOf(assignedUser)}));
175 }
176 }
177
178 private boolean isSame(int isKey, int isModifier) {
179 // an unassigned shortcut is different from any other shortcut
180 return isKey == assignedKey && isModifier == assignedModifier && assignedModifier != getGroupModifier(NONE);
181 }
182
183 public boolean isEvent(KeyEvent e) {
184 return getKeyStroke() != null && getKeyStroke().equals(
185 KeyStroke.getKeyStroke(e.getKeyCode(), e.getModifiers()));
186 }
187
188 /**
189 * use this to set a menu's mnemonic
190 */
191 public void setMnemonic(JMenu menu) {
192 if (assignedModifier == getGroupModifier(MNEMONIC) && getKeyStroke() != null && KeyEvent.getKeyText(assignedKey).length() == 1) {
193 menu.setMnemonic(KeyEvent.getKeyText(assignedKey).charAt(0)); //getKeyStroke().getKeyChar() seems not to work here
194 }
195 }
196 /**
197 * use this to set a buttons's mnemonic
198 */
199 public void setMnemonic(AbstractButton button) {
200 if (assignedModifier == getGroupModifier(MNEMONIC) && getKeyStroke() != null && KeyEvent.getKeyText(assignedKey).length() == 1) {
201 button.setMnemonic(KeyEvent.getKeyText(assignedKey).charAt(0)); //getKeyStroke().getKeyChar() seems not to work here
202 }
203 }
204 /**
205 * use this to set a actions's accelerator
206 */
207 public void setAccelerator(AbstractAction action) {
208 if (getKeyStroke() != null) {
209 action.putValue(AbstractAction.ACCELERATOR_KEY, getKeyStroke());
210 }
211 }
212
213 /**
214 * use this to get a human readable text for your shortcut
215 */
216 public String getKeyText() {
217 KeyStroke keyStroke = getKeyStroke();
218 if (keyStroke == null) return "";
219 String modifText = KeyEvent.getKeyModifiersText(keyStroke.getModifiers());
220 if ("".equals (modifText)) return KeyEvent.getKeyText (keyStroke.getKeyCode ());
221 return modifText + "+" + KeyEvent.getKeyText(keyStroke.getKeyCode ());
222 }
223
224 @Override
225 public String toString() {
226 return getKeyText();
227 }
228
229 ///////////////////////////////
230 // everything's static below //
231 ///////////////////////////////
232
233 // here we store our shortcuts
234 private static Map<String, Shortcut> shortcuts = new LinkedHashMap<>();
235
236 // and here our modifier groups
237 private static Map<Integer, Integer> groups= new HashMap<>();
238
239 // check if something collides with an existing shortcut
240 public static Shortcut findShortcut(int requestedKey, int modifier) {
241 if (modifier == getGroupModifier(NONE))
242 return null;
243 for (Shortcut sc : shortcuts.values()) {
244 if (sc.isSame(requestedKey, modifier))
245 return sc;
246 }
247 return null;
248 }
249
250 /**
251 * FOR PREF PANE ONLY
252 */
253 public static List<Shortcut> listAll() {
254 List<Shortcut> l = new ArrayList<>();
255 for(Shortcut c : shortcuts.values())
256 {
257 if(!"core:none".equals(c.shortText)) {
258 l.add(c);
259 }
260 }
261 return l;
262 }
263
264 public static final int NONE = 5000;
265 public static final int MNEMONIC = 5001;
266 public static final int RESERVED = 5002;
267 public static final int DIRECT = 5003;
268 public static final int ALT = 5004;
269 public static final int SHIFT = 5005;
270 public static final int CTRL = 5006;
271 public static final int ALT_SHIFT = 5007;
272 public static final int ALT_CTRL = 5008;
273 public static final int CTRL_SHIFT = 5009;
274 public static final int ALT_CTRL_SHIFT = 5010;
275
276 /* for reassignment */
277 private static int[] mods = {ALT_CTRL, ALT_SHIFT, CTRL_SHIFT, ALT_CTRL_SHIFT};
278 private static int[] keys = {KeyEvent.VK_F1, KeyEvent.VK_F2, KeyEvent.VK_F3, KeyEvent.VK_F4,
279 KeyEvent.VK_F5, KeyEvent.VK_F6, KeyEvent.VK_F7, KeyEvent.VK_F8,
280 KeyEvent.VK_F9, KeyEvent.VK_F10, KeyEvent.VK_F11, KeyEvent.VK_F12};
281
282 // bootstrap
283 private static boolean initdone = false;
284 private static void doInit() {
285 if (initdone) return;
286 initdone = true;
287 groups.put(NONE, -1);
288 groups.put(MNEMONIC, KeyEvent.ALT_DOWN_MASK);
289 groups.put(DIRECT, 0);
290 groups.put(ALT, KeyEvent.ALT_DOWN_MASK);
291 groups.put(SHIFT, KeyEvent.SHIFT_DOWN_MASK);
292 groups.put(CTRL, KeyEvent.CTRL_DOWN_MASK);
293 groups.put(ALT_SHIFT, KeyEvent.ALT_DOWN_MASK|KeyEvent.SHIFT_DOWN_MASK);
294 groups.put(ALT_CTRL, KeyEvent.ALT_DOWN_MASK|KeyEvent.CTRL_DOWN_MASK);
295 groups.put(CTRL_SHIFT, KeyEvent.CTRL_DOWN_MASK|KeyEvent.SHIFT_DOWN_MASK);
296 groups.put(ALT_CTRL_SHIFT, KeyEvent.ALT_DOWN_MASK|KeyEvent.CTRL_DOWN_MASK|KeyEvent.SHIFT_DOWN_MASK);
297
298 // (1) System reserved shortcuts
299 Main.platform.initSystemShortcuts();
300 // (2) User defined shortcuts
301 LinkedList<Shortcut> newshortcuts = new LinkedList<>();
302 for(String s : Main.pref.getAllPrefixCollectionKeys("shortcut.entry.")) {
303 newshortcuts.add(new Shortcut(s));
304 }
305
306 for(Shortcut sc : newshortcuts) {
307 if (sc.getAssignedUser()
308 && findShortcut(sc.getAssignedKey(), sc.getAssignedModifier()) == null) {
309 shortcuts.put(sc.getShortText(), sc);
310 }
311 }
312 // Shortcuts at their default values
313 for(Shortcut sc : newshortcuts) {
314 if (!sc.getAssignedUser() && sc.getAssignedDefault()
315 && findShortcut(sc.getAssignedKey(), sc.getAssignedModifier()) == null) {
316 shortcuts.put(sc.getShortText(), sc);
317 }
318 }
319 // Shortcuts that were automatically moved
320 for(Shortcut sc : newshortcuts) {
321 if (!sc.getAssignedUser() && !sc.getAssignedDefault()
322 && findShortcut(sc.getAssignedKey(), sc.getAssignedModifier()) == null) {
323 shortcuts.put(sc.getShortText(), sc);
324 }
325 }
326 }
327
328 private static int getGroupModifier(int group) {
329 Integer m = groups.get(group);
330 if(m == null)
331 m = -1;
332 return m;
333 }
334
335 private static int findModifier(int group, Integer modifier) {
336 if(modifier == null) {
337 modifier = getGroupModifier(group);
338 if (modifier == null) { // garbage in, no shortcut out
339 modifier = getGroupModifier(NONE);
340 }
341 }
342 return modifier;
343 }
344
345 // shutdown handling
346 public static boolean savePrefs() {
347 boolean changed = false;
348 for (Shortcut sc : shortcuts.values()) {
349 changed = changed | sc.save();
350 }
351 return changed;
352 }
353
354 /**
355 * FOR PLATFORMHOOK USE ONLY
356 *
357 * This registers a system shortcut. See PlatformHook for details.
358 */
359 public static Shortcut registerSystemShortcut(String shortText, String longText, int key, int modifier) {
360 if (shortcuts.containsKey(shortText))
361 return shortcuts.get(shortText);
362 Shortcut potentialShortcut = findShortcut(key, modifier);
363 if (potentialShortcut != null) {
364 // this always is a logic error in the hook
365 Main.error("CONFLICT WITH SYSTEM KEY "+shortText);
366 return null;
367 }
368 potentialShortcut = new Shortcut(shortText, longText, key, RESERVED, key, modifier, true, false);
369 shortcuts.put(shortText, potentialShortcut);
370 return potentialShortcut;
371 }
372
373 /**
374 * Register a shortcut.
375 *
376 * Here you get your shortcuts from. The parameters are:
377 *
378 * @param shortText an ID. re-use a {@code "system:*"} ID if possible, else use something unique.
379 * {@code "menu:*"} is reserved for menu mnemonics, {@code "core:*"} is reserved for
380 * actions that are part of JOSM's core. Use something like
381 * {@code <pluginname>+":"+<actionname>}.
382 * @param longText this will be displayed in the shortcut preferences dialog. Better
383 * use something the user will recognize...
384 * @param requestedKey the key you'd prefer. Use a {@link KeyEvent KeyEvent.VK_*} constant here.
385 * @param requestedGroup the group this shortcut fits best. This will determine the
386 * modifiers your shortcut will get assigned. Use the constants defined above.
387 */
388 public static Shortcut registerShortcut(String shortText, String longText, int requestedKey, int requestedGroup) {
389 return registerShortcut(shortText, longText, requestedKey, requestedGroup, null);
390 }
391
392 // and now the workhorse. same parameters as above, just one more
393 private static Shortcut registerShortcut(String shortText, String longText, int requestedKey, int requestedGroup, Integer modifier) {
394 doInit();
395 Integer defaultModifier = findModifier(requestedGroup, modifier);
396 if (shortcuts.containsKey(shortText)) { // a re-register? maybe a sc already read from the preferences?
397 Shortcut sc = shortcuts.get(shortText);
398 sc.setLongText(longText); // or set by the platformHook, in this case the original longText doesn't match the real action
399 sc.saveDefault();
400 return sc;
401 }
402 Shortcut conflict = findShortcut(requestedKey, defaultModifier);
403 if (conflict != null) {
404 for (int m : mods) {
405 for (int k : keys) {
406 int newmodifier = getGroupModifier(m);
407 if ( findShortcut(k, newmodifier) == null ) {
408 Shortcut newsc = new Shortcut(shortText, longText, requestedKey, m, k, newmodifier, false, false);
409 Main.info(tr("Silent shortcut conflict: ''{0}'' moved by ''{1}'' to ''{2}''.",
410 shortText, conflict.getShortText(), newsc.getKeyText()));
411 newsc.saveDefault();
412 shortcuts.put(shortText, newsc);
413 return newsc;
414 }
415 }
416 }
417 } else {
418 Shortcut newsc = new Shortcut(shortText, longText, requestedKey, requestedGroup, requestedKey, defaultModifier, true, false);
419 newsc.saveDefault();
420 shortcuts.put(shortText, newsc);
421 return newsc;
422 }
423
424 return null;
425 }
426
427 /**
428 * Replies the platform specific key stroke for the 'Copy' command, i.e.
429 * 'Ctrl-C' on windows or 'Meta-C' on a Mac. null, if the platform specific
430 * copy command isn't known.
431 *
432 * @return the platform specific key stroke for the 'Copy' command
433 */
434 public static KeyStroke getCopyKeyStroke() {
435 Shortcut sc = shortcuts.get("system:copy");
436 if (sc == null) return null;
437 return sc.getKeyStroke();
438 }
439
440 /**
441 * Replies the platform specific key stroke for the 'Paste' command, i.e.
442 * 'Ctrl-V' on windows or 'Meta-V' on a Mac. null, if the platform specific
443 * paste command isn't known.
444 *
445 * @return the platform specific key stroke for the 'Paste' command
446 */
447 public static KeyStroke getPasteKeyStroke() {
448 Shortcut sc = shortcuts.get("system:paste");
449 if (sc == null) return null;
450 return sc.getKeyStroke();
451 }
452
453 /**
454 * Replies the platform specific key stroke for the 'Cut' command, i.e.
455 * 'Ctrl-X' on windows or 'Meta-X' on a Mac. null, if the platform specific
456 * 'Cut' command isn't known.
457 *
458 * @return the platform specific key stroke for the 'Cut' command
459 */
460 public static KeyStroke getCutKeyStroke() {
461 Shortcut sc = shortcuts.get("system:cut");
462 if (sc == null) return null;
463 return sc.getKeyStroke();
464 }
465}
Note: See TracBrowser for help on using the repository browser.