How To Add Keybindings

Adding configurable keybindings to new aspects of the Jitsi’s UI isn’t terribly difficult but can be tricky if you don’t know where to look. The KeybindingsService handles the management and persistence of bindings for use either by InputMaps or KeyListeners (if you’re new to Swing’s use of keybindings then see How to Use Key Bindings). This page exemplifies usage for:

net.java.sip.communicator.impl.gui.main.chat.toolBars.EditTextToolBar

which uses shortcuts for the fonts. All new binding sets need the following components:

  1. Unique string identifier for the component of the UI to which the bindings belong. For instance the identifier for the EditTextToolBar is chatToolbar.
  2. Strings associated with the actions to be performed. For instance the EditTextToolBar uses bold, italic, and underline. The string identifiers used by your InputMap and ActionMap later will be <component>-<action> (ex. chatToolbar-italic).
  3. i18n translations that are a reader friendly description of the action for the chooser. As of this writing we only have English and the EditTextToolBar simply uses: “Bold”, “Italic”, and “Underline”.
  4. Default keybindings. This uses the string values described in getKeyStroke(String) method of KeyStroke.
  5. The actions to be fired when key’s pressed. This is often easiest via AbstractAction.

To add in a new keybinding set:

  • Add the default bindings to resources/config/defaults.properties. This consists of mapping

net.java.sip.communicator.impl.gui.keybindings.<component>.<action>.<field>

to the appropriate values where the fields are:

  • index: ordering in which the bindings are presented
  • shortcut: KeyStroke string for default binding
  • shortcutAlt: Optional alternative default KeyStroke binding.

For instance the EditTextToolBar contributes the following:

net.java.sip.communicator.impl.gui.keybindings.chatToolbar.bold.index=0
net.java.sip.communicator.impl.gui.keybindings.chatToolbar.bold.shortcut=ctrl pressed B
net.java.sip.communicator.impl.gui.keybindings.chatToolbar.bold.shortcutAlt=ctrl pressed F
net.java.sip.communicator.impl.gui.keybindings.chatToolbar.italic.index=1
net.java.sip.communicator.impl.gui.keybindings.chatToolbar.italic.shortcut=ctrl pressed I
net.java.sip.communicator.impl.gui.keybindings.chatToolbar.italic.shortcutAlt=ctrl pressed K
net.java.sip.communicator.impl.gui.keybindings.chatToolbar.underline.index=2
net.java.sip.communicator.impl.gui.keybindings.chatToolbar.underline.shortcut=ctrl pressed U
  • Make your new bindings available for internal use. To do this you’ll need to edit

net.java.sip.communicator.service.keybindings.KeybindingSet

adding the component to the Category enum. For instance the following was added for the EditTextToolBar: CHAT_TOOLBAR(“chatToolbar”)

  • Add the mapping between the internal identifier and the i18n strings for the UI in:

resources/languages/resources.properties

under the “key binding chooser” heading. For instance the following entries were added for the EditTextToolBar:

bold=Bold
italic=Italic
underline=Underline

Changes made in your class:

If your class extends net.java.sip.communicator.impl.gui.main.MainFrame then using the bindings is trivial. Simply use the setKeybindingInput(KeybindingSet.Category) and addKeybindingAction(String, Action) methods to associate the bindings with the actions. For instance if the EditTextToolBar extended MainFrame then it would use something like:

this.setKeybindingInput(KeybindingSet.Category.CHAT_TOOLBAR);
this.addKeybindingAction("chatToolbar-bold", new BoldAction());
this.addKeybindingAction("chatToolbar-italic", new ItalicAction());
this.addKeybindingAction("chatToolbar-underline", new UnderlineAction());

If not then there’s still some work to do.

  1. Get an instance of the KeybindingService via the getKeybindingService() method of the UIService.
  2. Snag an instance of your component’s KeybindingSet which stores the current bindings the user’s selected. You can get it via the getBindings(KeybindingSet.Category) method of the KeybindingService.
  3. Make your class implement the Observer interface and add it to the KeybindingSet. This will notify your class when the bindings have been changed.
  4. Create a method for resetting the keybindings. This should simply empty your class’ InputMap and re-add the bindings provided by your KeybindingSet. Call this method both when you initialize and for the update(Observable, Object) method you implement as an Observer.
  5. Add an ActionMap associating the <component>-<action> values to the actions they should perform.

For instance:

public class YourClass
    extends JPanel
    implements Observer
{
    private KeybindingSet bindings;

    YourClass(UIService uiService)
    {
        KeybindingsService service = uiService.getKeybindingsService();
        this.bindings = service.getBindings(KeybindingSet.Category.CHAT_TOOLBAR);
        this.bindings.addObserver(this);
        resetBindings(); // initializes InputMap

        ActionMap actionMap = getActionMap();
        actionMap.put("chatToolbar-bold", new BoldAction());
        actionMap.put("chatToolbar-italic", new ItalicAction());
        actionMap.put("chatToolbar-underline", new UnderlineAction());
    }

    // replaces input map entries with new bindings
    private void resetBindings()
    {
        InputMap inputMap = getInputMap();
        inputMap.clear();
        HashMap <KeyStroke, String> currentBindings = this.bindings.getBindings();
        for (KeyStroke shortcut : currentBindings.keySet()) 
        {
            inputMap.put(shortcut, currentBindings.get(shortcut));
        }
    }

    // Listens for changes in binding sets so they can be reflected in the InputMap
    public void update(Observable obs, Object arg)
    {
        if (obs instanceof KeybindingSet) resetBindings();
    }
}

That’s it. Your new bindings should now be included in a new tab under Settings > Keybindings. Changes applied here will be reflected in your class and those changes will be preserved within the user’s preferences.