FileDocCategorySizeDatePackage
KeyBindings.javaAPI DocAzureus 3.0.3.416665Wed Jul 19 23:28:12 BST 2006org.gudy.azureus2.ui.swt

KeyBindings

public final class KeyBindings extends Object

Facilitates localization-specific and platform-specific keyboard shortcut handling through the use of keybinding values.

A keybinding value is a line of String that can be specified in the localization bundle properties file for a particular menu item. To do so, a localization key/value pair is used, with the key being the same as menu item's key plus ".keybinding".

For instance, if a keyboard shortcut needs to be specified for "MainWindow.menu.file.open", then a key/value pair with the key of "MainWindow.menu.file.open.keybinding" is created. The value is what would be used as the keyboard accelerator, with the following special values:

  • Meta (or Cmd) - The "Meta" modifier; this is "Command" on OS X, and "Control" on all other platforms
  • Ctrl - The "Control" modifier on all platforms. Use this only if you need to enforce the use of Control.
  • Alt (or Opt) - The "Alt" modifier
  • Shift - The "Shift" modifier
  • Ins - Insert
  • Backspace
  • Del - Delete
  • Esc - Escape
  • PgUp - Page Up
  • PgDn - Page Down
  • Left - The left arrow
  • Up - The up arrow
  • Right - The right arrow
  • Down - The down arrow
  • Home
  • End
  • Tab
  • Fx, where x is an integer from 1 to 15 (inclusive) - The function key (F1-F15)

Other valid values can be typed as is or as the Unicode representation. For security reasons, this is initially set with a very conservative scope, including alphanumerics, \, =, -, (comma), (period), and `.

As of version 1.2, keybindings are only set if the following conditions are met:

  1. A function key is set or
  2. Meta, Alt, or Ctrl is set (or their variations)

The keys were chosen to more conveniently address the issue on platforms like Windows where vanilla SWT does not display the shortcuts, as opposed to higher-level API like JFace or some platforms' native rendering.

For example, if File / Open / .torrent File is set to be Meta+O (Command+O or Ctrl+O), then in MessageBundle.properties (or the localization- specific equivalent), MainWindow.menu.file.open.torrent.keybinding=Meta+O will be entered. The label will be adjusted to Ctrl on non-OS X platforms (OS X will draw the glyph for Cmd).

As another example, if File / Exit is set to be Alt+F4, then in MessageBundle.properties (or the localization-specific equivalent), MainWindow.menu.file.exit.keybinding=Alt+F4 will be entered.

To accommodate for the variety of locales and platforms running on Azureus, platforms and localizations can "override" the default keybinding value. The order of parsing is as follows:

  1. If a localized keybinding value exists for the current locale and platform, it is used
  2. If the above is not found, this method looks for a keybinding value for the current locale without platform specificity
  3. If the above is not found, this method looks for a keybinding value for the default locale and the currently running platform
  4. If the above is not found, this method looks for a keybinding value for the default locale without platform specificity
  5. If the above is not found, no accelerator is set for the MenuItem

For instance, to refer to the above example, if the Mac OS X target for Azureus wants to handle File / Exit as Command+Q, then MainWindow.menu.file.exit.keybinding.mac=Meta+Q is entered. If it is not entered, the value for the 'default' key MainWindow.menu.file.exit.keybinding will be used.

The platform suffix can be attached to the end of the localization key. Valid suffixes are:

  • .linux - Linux
  • .mac - Mac OS X
  • .windows - Windows

author
CrazyAlchemist
version
1.3 Added removeAccelerator

Fields Summary
private static final Pattern
FUNC_EXP
private static final Pattern
SANCTIONED_EXP
private static final String[]
SPECIAL_KEYS
private static final int[]
SPECIAL_VALUES
private static final String
DELIM
private static final String
DELIM_EXP
Constructors Summary
Methods Summary
private static java.lang.StringgetPlatformKeySuffix()

Gets the localization key suffix for the running platform for keybinding retrieval

For now, as is with Azureus' Constants behaviour, supported platforms are Linux, Mac OS X, and Windows only

return
The platform key suffix; or an empty string on an unsupported platform


                                                        
       
    
        if(Constants.isLinux)
            return ".linux";
        else if(Constants.isSolaris)
          return ".solaris";
        else if(Constants.isUnix)
          return ".unix";
        else if(Constants.isFreeBSD)
          return ".freebsd";
        else if(Constants.isOSX)
            return ".mac";
        else if(Constants.isWindows)
            return ".windows";

        return "";
    
public static voidmain(java.lang.String[] args)
Runs simple tests on KeyBindings keybinding values

param
args Command-line arguments; they are not used

        System.out.println(parseKeyBinding("Ctrl+1").name); // meta+1
        System.out.println(parseKeyBinding("Ctrl+F12").name); // meta+f12
        System.out.println(parseKeyBinding("Ctrl+F4").name); // meta+f4

        System.out.println("Meta+Shift+O");
        System.out.println(parseKeyBinding("Ctrl+Shift+O").accelerator); // meta+shift+o
        System.out.println(parseKeyBinding("Shift+Ctrl+O").accelerator); // meta+shift+o
        System.out.println(SWT.MOD1 | SWT.SHIFT | 'O"); // meta+shift+o

        System.out.println("Meta+Shift+o");
        System.out.println(SWT.MOD1 | SWT.SHIFT | 'o"); // meta+shift+o
    
private static org.gudy.azureus2.ui.swt.KeyBindings$KeyBindingInfoparseKeyBinding(java.lang.String keyBindingValue)
Parses the keybinding string according to the specifications documented at this class and gets the SWT value equivalent for keyboard accelerator settings.

param
keyBindingValue Keybinding value
return
A KeyBindingInfo object, which consists of the SWT accelerator and its display name

        if(keyBindingValue.length() < 1)
            return new KeyBindingInfo(null, SWT.NONE);

        // initialize with nothing
        int swtAccelerator = SWT.NONE;

        final String[] tmpValues = keyBindingValue.split(DELIM_EXP);
        final boolean[] specVisited = new boolean[SPECIAL_KEYS.length]; // flag for speed optimization
        boolean funcVisited = false;

        final StringBuffer displayValue = new StringBuffer(keyBindingValue.length() + 2); // allocate display string
        displayValue.append('\t");

        for (int i = 0; i < tmpValues.length; i++)
        {
            final String value = tmpValues[i];
            boolean matched = false;

             // process special keys first
            for(int j = 0; j < SPECIAL_KEYS.length; j++)
            {
                if(!specVisited[j] && SPECIAL_KEYS[j].equalsIgnoreCase(value))
                {
                    swtAccelerator = swtAccelerator | SPECIAL_VALUES[j];

                    // special-case meta; a generalized solution would be warranted if:
                    // a) additional special modifiers persist or
                    // b) other platforms have special labeling requirements or
                    // c) SWT changes its lower-level API so shortcut labels are no longer custom drawn on Win etc.
                    if(SPECIAL_KEYS[j].equalsIgnoreCase("Meta"))
                        displayValue.append(Constants.isOSX ? "Cmd" : "Ctrl").append(DELIM);
                    else
                        displayValue.append(SPECIAL_KEYS[j]).append(DELIM);

                    // mark flags
                    specVisited[j] = true;
                    matched = true;
                    break;
                }
            }

            if(matched)
                continue;

            // special treatment for function keys
            if(!funcVisited)
            {
                final Matcher funcMatcher = FUNC_EXP.matcher(value);
                if(funcMatcher.find() && funcMatcher.start() == 0 && funcMatcher.end() == value.length())
                {
                    final int funcVal = Integer.parseInt(funcMatcher.group(2));

                    // SWT.F1 is (1 << 24) + 10
                    swtAccelerator = swtAccelerator | ((1 << 24) + (9 + funcVal));
                    displayValue.append(funcMatcher.group(0)).append(DELIM);

                    funcVisited = true;
                    matched = true;
                }
            }

            if(matched)
                continue;

            final Matcher valMatcher = SANCTIONED_EXP.matcher(value);
            if(valMatcher.find() && valMatcher.start() == 0)
            {
                final char c = valMatcher.group().charAt(0);

                // avoid possible duplicates (\t is index 0)
                final int subStrIndex = displayValue.indexOf(c + DELIM);
                if(subStrIndex == 1 || (subStrIndex > 1 && displayValue.substring(subStrIndex - 1, subStrIndex).equals(DELIM)))
                    continue;

                swtAccelerator = swtAccelerator | c;
                displayValue.append(c).append(DELIM);
            }
        }

        if(funcVisited || specVisited[0] || specVisited[1] || specVisited[2] || specVisited[3] || specVisited[4]) // special case - be a bit careful for now
            return new KeyBindingInfo(displayValue.substring(0, displayValue.length() - 1), swtAccelerator);
        else
            return new KeyBindingInfo(null, SWT.NONE);
    
public static voidremoveAccelerator(org.eclipse.swt.widgets.MenuItem menu, java.lang.String localizationKey)

Removes the keyboard accelerator for a SWT MenuItem

param
menu SWT MenuItem
param
localizationKey The MenuItem's localization key for the localization resource bundle

        setAccelerator(menu, new KeyBindingInfo("", SWT.NONE));
        Messages.setLanguageText(menu, localizationKey);
    
public static voidsetAccelerator(org.eclipse.swt.widgets.MenuItem menu, java.lang.String localizationKey)

Sets the keyboard accelerator for a SWT MenuItem.

There is a specific order of accelerator setting in consideration with different platforms and localizations. Specifically:

  1. If a localized keybinding value exists for the current locale and platform, it is used
  2. If the above is not found, this method looks for a keybinding value for the current locale without platform specificity
  3. If the above is not found, this method looks for a keybinding value for the default locale and the currently running platform
  4. If the above is not found, this method looks for a keybinding value for the default locale without platform specificity
  5. If the above is not found, no accelerator is set for the MenuItem

param
menu SWT MenuItem
param
localizationKey The MenuItem's localization key for the localization resource bundle

        localizationKey += ".keybinding";
        final String platformSpecificKey = localizationKey + getPlatformKeySuffix();

        // first, check for platform-specific, localization-specific binding
        if(MessageText.keyExists(platformSpecificKey))
        {
            setAccelerator(menu, parseKeyBinding(MessageText.getString(platformSpecificKey)));
        }
        else if(MessageText.keyExists(localizationKey)) // platform-independent, localization-specific binding
        {
            setAccelerator(menu, parseKeyBinding(MessageText.getString(localizationKey)));
        }
        else if(!MessageText.isCurrentLocale(MessageText.LOCALE_DEFAULT))
        {
            // default locale

            // platform-specific first
            if(MessageText.keyExistsForDefaultLocale(platformSpecificKey))
            {
                setAccelerator(menu, parseKeyBinding(MessageText.getDefaultLocaleString(platformSpecificKey)));
            }
            else if(MessageText.keyExistsForDefaultLocale(localizationKey))
            {
                 // default locale, platform-independent
                setAccelerator(menu, parseKeyBinding(MessageText.getDefaultLocaleString(localizationKey)));
            }
        }
    
private static voidsetAccelerator(org.eclipse.swt.widgets.MenuItem menu, org.gudy.azureus2.ui.swt.KeyBindings$KeyBindingInfo kbInfo)
Helper method to set a keyboard accelerator for a MenuItem. If kbInfo is SWT.NONE, no accelerator will be set.

param
menu SWT MenuItem
param
kbInfo KeyBindingInfo object, which contains the SWT accelerator value and its display name

        if(kbInfo.accelerator != SWT.NONE)
        {
            menu.setAccelerator(kbInfo.accelerator);

            // SWT on OS X now uses native drawing
            if(!Constants.isOSX && !menu.getText().endsWith(kbInfo.name))
                menu.setText(menu.getText() + kbInfo.name);
        }