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

Last change on this file since 5299 was 5266, checked in by bastiK, 12 years ago

fixed majority of javadoc warnings by replacing "{@see" by "{@link"

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