[Swing] Application Wide Hotkeys
I'm working on a Swing application and I need the ability to have application wide hotkeys. Basically that means no matter what I am currently doing in my app specific keystrokes always do the same thing. For example I am mimicking IDEA's CTRL+N functionallity. When CTRL+N is pressed a dialog opens with a textfield that allows me to type in a search string. As I type a list below the textfield populates with what my app thinks I am looking for.
I found most of the GlobalHotkeyManager code somewhere on the web. I honestly can't remember where right now. If it looks familiar to you then let me know and I'll give props. Also, if anyone has alternative methods that might be a better approach please let me know.
public class GlobalHotkeyManager extends EventQueue {
private static final GlobalHotkeyManager instance = new GlobalHotkeyManager();
private final InputMap keyStrokes = new InputMap();
private final ActionMap actions = new ActionMap();
private final static String LOOKUPDIALOG_KEY = "LOOKUP_DIALOG";
private final KeyStroke lookupDialogHotkey = KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_MASK, false);
static {
// here we register ourselves as a new link in the chain of
// responsibility
Toolkit.getDefaultToolkit().getSystemEventQueue().push(instance);
}
private GlobalHotkeyManager() {
getInputMap().put(lookupDialogHotkey, LOOKUPDIALOG_KEY);
getActionMap().put(LOOKUPDIALOG_KEY, new LookupDialogAction());
} // One is enough - singleton
public static GlobalHotkeyManager getInstance() {
return instance;
}
public InputMap getInputMap() {
return keyStrokes;
}
public ActionMap getActionMap() {
return actions;
}
protected void dispatchEvent(AWTEvent event) {
if (event instanceof KeyEvent) {
// KeyStroke.getKeyStrokeForEvent converts an ordinary KeyEvent
// to a keystroke, as stored in the InputMap. Keep in mind that
// Numpad keystrokes are different to ordinary keys, i.e. if you
// are listening to
KeyStroke ks = KeyStroke.getKeyStrokeForEvent((KeyEvent) event);
//if (DEBUG) System.out.println("KeyStroke=" + ks);
String actionKey = (String) keyStrokes.get(ks);
if (actionKey != null) {
//if (DEBUG) System.out.println("ActionKey=" + actionKey);
Action action = actions.get(actionKey);
if (action != null && action.isEnabled()) {
// I'm not sure about the parameters
action.actionPerformed(
new ActionEvent(event.getSource(), event.getID(),
actionKey, ((KeyEvent) event).getModifiers()));
return; // consume event
}
}
}
super.dispatchEvent(event); // let the next in chain handle event
}
}
I left the relevant comments in the code so that it should be fairly straight forward. GlobalHotkeyManager is a singleton because we only ever want one manager running per application instance. I have an Application class I use that acts as sort of Session object for the application. It has getter and setter methods for global components that I need access to and it saves me from having huge constructors where I might have to pass in 10 different objects just to display a particular screen. The GlobalHotkeyManager is created and then set in my Application class.
Application.getInstance().setHotkeyManager(GlobalHotkeyManager.getInstance());
The LookupDialogAction class is really simple.
public class LookupDialogAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
setEnabled(false);
new LookupDialog(Application.getInstance().getMainFrame(), true);
setEnabled(true);
}
}
I haven't yet completed the LookupDialog code that actually does the work once the dialog is visible. I'm planning that for another article because I am going to use it to demonstrate a great API I found called GlazedLists.