/**
* Copyright (c) 2010 Marc A. Paradise
*
* This file is part of "BBSSH"
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
package org.bbssh.keybinding;
import net.rim.device.api.i18n.ResourceBundle;
import net.rim.device.api.system.KeypadListener;
import net.rim.device.api.ui.Keypad;
import net.rim.device.api.util.IntHashtable;
import org.bbssh.i18n.BBSSHResource;
import org.bbssh.platform.PlatformServicesProvider;
import org.bbssh.terminal.VT320;
/**
* @todo this entire class is a mess- a much better refactor is required when time allows.
*/
public class KeyBindingHelper implements BBSSHResource {
// Here we define many 'virtual key' mappings that either don't have corresponding
// constants in the platform; or may not exist in a specific platform version.
// @todo do we just want to add virt keys for all possible keys, instead of just
// the ones missing or platform specific?
// These are safely clear of the standard key mappings
// though they DO conflict with key status - so they can't
// be used in conjunction as int values (ie, packed long only).
public static final int KEY_NAV_UP = 0x01 << 16;
public static final int KEY_NAV_DOWN = 0x02 << 16;
public static final int KEY_NAV_LEFT = 0x03 << 16;
public static final int KEY_NAV_RIGHT = 0x04 << 16;
public static final int KEY_NAV_CLICK = 0x05 << 16;
public static final int KEY_CURRENCY = 0x06 << 16;
public static final int KEY_ZERO = 0x07 << 16;
public static final int KEY_CAMERA_FOCUS = 0x08 << 16;
public static final int KEY_LOCK = 0x09 << 16;
// We use our own constants so that we can include generic binding support, without having to
// split the keybinding core/engine into OS-specific components.
public static final int KEY_TOUCH_ID_START = 0x10 << 16;
// 0x10 available - touch
// 0x11 available - touch
// 0x12 available - touch
// 0x13 available - touch
public static final int KEY_TOUCH_PINCH_IN = 0x15 << 16;
public static final int KEY_TOUCH_PINCH_OUT = 0x16 << 16;
public static final int KEY_TOUCH_SWIPE_NORTH = 0x20 << 16;
public static final int KEY_TOUCH_SWIPE_SOUTH = 0x21 << 16;
public static final int KEY_TOUCH_SWIPE_EAST = 0x22 << 16;
public static final int KEY_TOUCH_SWIPE_WEST = 0x23 << 16;
public static final int KEY_TOUCH_SWIPE_SOUTHEAST = 0x24 << 16;
public static final int KEY_TOUCH_SWIPE_SOUTHWEST = 0x25 << 16;
public static final int KEY_TOUCH_SWIPE_NORTHEAST = 0x26 << 16;
public static final int KEY_TOUCH_SWIPE_NORTHWEST = 0x27 << 16;
public static final int KEY_NAV_SWIPE_NORTH = 0x30 << 16;
public static final int KEY_NAV_SWIPE_SOUTH = 0x31 << 16;
public static final int KEY_NAV_SWIPE_EAST = 0x32 << 16;
public static final int KEY_NAV_SWIPE_WEST = 0x33 << 16;
public static final int KEY_TOUCH_TAP_CENTER = 0x40 << 16;
public static final int KEY_TOUCH_TAP_NORTH = 0x41 << 16;
public static final int KEY_TOUCH_TAP_SOUTH = 0x42 << 16;
public static final int KEY_TOUCH_TAP_EAST = 0x43 << 16;
public static final int KEY_TOUCH_TAP_WEST = 0x44 << 16;
public static final int KEY_TOUCH_TAP_SOUTHEAST = 0x45 << 16;
public static final int KEY_TOUCH_TAP_SOUTHWEST = 0x46 << 16;
public static final int KEY_TOUCH_TAP_NORTHEAST = 0x47 << 16;
public static final int KEY_TOUCH_TAP_NORTHWEST = 0x48 << 16;
public static final int KEY_TOUCH_DBTAP_CENTER = 0x50 << 16;
public static final int KEY_TOUCH_DBTAP_NORTH = 0x51 << 16;
public static final int KEY_TOUCH_DBTAP_SOUTH = 0x52 << 16;
public static final int KEY_TOUCH_DBTAP_EAST = 0x53 << 16;
public static final int KEY_TOUCH_DBTAP_WEST = 0x54 << 16;
public static final int KEY_TOUCH_DBTAP_SOUTHEAST = 0x55 << 16;
public static final int KEY_TOUCH_DBTAP_SOUTHWEST = 0x56 << 16;
public static final int KEY_TOUCH_DBTAP_NORTHEAST = 0x57 << 16;
public static final int KEY_TOUCH_DBTAP_NORTHWEST = 0x58 << 16;
public static final int KEY_TOUCH_CLICK_CENTER = 0x60 << 16;
public static final int KEY_TOUCH_CLICK_NORTH = 0x61 << 16;
public static final int KEY_TOUCH_CLICK_SOUTH = 0x62 << 16;
public static final int KEY_TOUCH_CLICK_EAST = 0x63 << 16;
public static final int KEY_TOUCH_CLICK_WEST = 0x64 << 16;
public static final int KEY_TOUCH_CLICK_SOUTHEAST = 0x65 << 16;
public static final int KEY_TOUCH_CLICK_SOUTHWEST = 0x66 << 16;
public static final int KEY_TOUCH_CLICK_NORTHEAST = 0x67 << 16;
public static final int KEY_TOUCH_CLICK_NORTHWEST = 0x68 << 16;
public static final int KEY_TOUCH_DBCLICK_CENTER = 0x70 << 16;
public static final int KEY_TOUCH_DBCLICK_NORTH = 0x71 << 16;
public static final int KEY_TOUCH_DBCLICK_SOUTH = 0x72 << 16;
public static final int KEY_TOUCH_DBCLICK_EAST = 0x73 << 16;
public static final int KEY_TOUCH_DBCLICK_WEST = 0x74 << 16;
public static final int KEY_TOUCH_DBCLICK_SOUTHEAST = 0x75 << 16;
public static final int KEY_TOUCH_DBCLICK_SOUTHWEST = 0x76 << 16;
public static final int KEY_TOUCH_DBCLICK_NORTHEAST = 0x77 << 16;
public static final int KEY_TOUCH_DBCLICK_NORTHWEST = 0x78 << 16;
public static final int KEY_TOUCH_HOVER_CENTER = 0x80 << 16;
public static final int KEY_TOUCH_HOVER_NORTH = 0x81 << 16;
public static final int KEY_TOUCH_HOVER_SOUTH = 0x82 << 16;
public static final int KEY_TOUCH_HOVER_EAST = 0x83 << 16;
public static final int KEY_TOUCH_HOVER_WEST = 0x84 << 16;
public static final int KEY_TOUCH_HOVER_SOUTHEAST = 0x85 << 16;
public static final int KEY_TOUCH_HOVER_SOUTHWEST = 0x86 << 16;
public static final int KEY_TOUCH_HOVER_NORTHEAST = 0x87 << 16;
public static final int KEY_TOUCH_HOVER_NORTHWEST = 0x88 << 16;
/**
* Add this to the 'touch swipe' value to get 'nav swipe' mappings for the same directions; and add to TAP value to
* get DOUBLE_TAP value. Add *2 to get CLICK value, *3 for DBCLICK, and *4 for HOVER
*/
public static final int KEY_MODE_ADJUST = 0x10 << 16;
public static final int KEY_MODE_MULTIPLIER_TAP = 0;
public static final int KEY_MODE_MULTIPLIER_DOUBLE_TAP = 1;
public static final int KEY_MODE_MULTIPLIER_CLICK = 2;
public static final int KEY_MODE_MULTIPLIER_DOUBLE_CLICK = 3;
public static final int KEY_MODE_MULTIPLIER_HOVER = 4;
public static final int KEY_MODE_MULTIPLIER_CLICK_REPEAT = 5;
public static final int KEY_TOUCH_ID_END = 0x9F << 16;
// These are used in allowing HW_LAYOUT_REDUCED_24 keypads to use
// each key as a binding.
public static final int KEY_R24_QW = 0xA0 << 16; // 'Q'
public static final int KEY_R24_ER = 0xA1 << 16; // 'E'
public static final int KEY_R24_TY = 0xA2 << 16; // 'T'
public static final int KEY_R24_UI = 0xA3 << 16; // 'U'
public static final int KEY_R24_OP = 0xA4 << 16; // 'O'
public static final int KEY_R24_AS = 0xA5 << 16; // 'A'
public static final int KEY_R24_DF = 0xA6 << 16; // 'D'
public static final int KEY_R24_GH = 0xA7 << 16; // 'G'
public static final int KEY_R24_JK = 0xA8 << 16; // 'J'
public static final int KEY_R24_L = 0xA9 << 16; // 'L'
public static final int KEY_R24_ZX = 0xAA << 16; // 'Z'
public static final int KEY_R24_CV = 0xAB << 16; // 'C'
public static final int KEY_R24_BN = 0xAC << 16; // 'B'
public static final int KEY_R24_0 = 0xAD << 16; // ' '
public static final int KEY_R24_M = 0xAE << 16; // 'M'
public static final int KEY_ITUT_1 = 0xB0 << 16; // 1!?,. 'Q'
public static final int KEY_ITUT_2 = 0xB1 << 16; // abc 'T'
public static final int KEY_ITUT_3 = 0xB2 << 16; // def 'O'
public static final int KEY_ITUT_4 = 0xB3 << 16; // ghi 'A'
public static final int KEY_ITUT_5 = 0xB4 << 16; // jkl 'G'
public static final int KEY_ITUT_6 = 0xB5 << 16; // mno 'L'
public static final int KEY_ITUT_7 = 0xB6 << 16; // pqrs 'Z'
public static final int KEY_ITUT_8 = 0xB7 << 16; // tuv 'B'
public static final int KEY_ITUT_9 = 0xB8 << 16; // wxyz 'W'
public static final int KEY_ITUT_0 = 0xB9 << 16; // ' ' ' '
/** Identical to Keypad.HW_LAYOUT_ITUT introduced in 6.0 and value used in 5.0 */
public static final int HW_LAYOUT_ITUT = 1230263636;
/** Keypad 5.0: public static final int KEY_FORWARD = 4100; */
public static final int KEY_FORWARD_SDK = 4100;
/** Keypad 5.0: public static final int KEY_BACKWARD = 4101; */
public static final int KEY_BACKWARD_SDK = 4101;
/** Keypad 4.7: public static final int KEY_CAMERA_FOCUS = 211 */
public static final int KEY_CAMERA_FOCUS_SDK = 211;
/** Keypad 5.0: public static final int KEY_LOCK = 4099 */
public static final int KEY_LOCK_SDK = 4099;
/**
* Keypad 4.5: public static final int KEY_DELETE = 127; For clarity, as KEY_DELETE is actually "SYM" in all models
* that we are concerned with.
*/
public static final int KEY_SYM = Keypad.KEY_DELETE;
// @todo Okay, this is sloppy too. ACtually... ALL of these strings belong in the RRC.
public static String[] FONT_CHANGE_CHOICES = new String[] { "Increase", "Decrease" };
// Categories
public static final int CAT_TERMINAL = 0;
public static final int CAT_TOUCH_TAP = 1;
public static final int CAT_TOUCH_DOUBLE_TAP = 2;
public static final int CAT_TOUCH_CLICK = 3;
public static final int CAT_TOUCH_DOUBLE_CLICK = 4;
public static final int CAT_TOUCH_GESTURE = 5;
public static final int CAT_TOUCH_HOVER = 6;
public static final int CAT_MEDIA = 7;
public static final int CAT_PHONE = 8;
public static final int CAT_KEYBOARD = 9;
public static final int CAT_NAV = 10;
/**
* Non-directional terminal keys.
*/
public static TerminalKey[] specialTerminalKeys = new TerminalKey[] {
new TerminalKey(VT320.VK_TAB, VTKEYNAME_TAB),
new TerminalKey(VT320.VK_DELETE, VTKEYNAME_DELETE),
new TerminalKey(VT320.VK_ENTER, VTKEYNAME_ENTER),
new TerminalKey(VT320.VK_CLEAR, VTKEYNAME_CLEAR),
new TerminalKey(VT320.VK_PAUSE, VTKEYNAME_PAUSE),
new TerminalKey(VT320.VK_SPACE, VTKEYNAME_SPACE),
new TerminalKey(VT320.VK_ESCAPE, VTKEYNAME_ESCAPE),
new TerminalKey(VT320.VK_BACK_SPACE, VTKEYNAME_BACK_SPACE),
new TerminalKey(VT320.VK_PAGE_UP, VTKEYNAME_PAGE_UP),
new TerminalKey(VT320.VK_PAGE_DOWN, VTKEYNAME_PAGE_DOWN),
new TerminalKey(VT320.VK_HOME, VTKEYNAME_HOME),
new TerminalKey(VT320.VK_END, VTKEYNAME_END),
new TerminalKey(VT320.VK_F1, VTKEYNAME_F1),
new TerminalKey(VT320.VK_F2, VTKEYNAME_F2),
new TerminalKey(VT320.VK_F3, VTKEYNAME_F3),
new TerminalKey(VT320.VK_F4, VTKEYNAME_F4),
new TerminalKey(VT320.VK_F5, VTKEYNAME_F5),
new TerminalKey(VT320.VK_F6, VTKEYNAME_F6),
new TerminalKey(VT320.VK_F7, VTKEYNAME_F7),
new TerminalKey(VT320.VK_F8, VTKEYNAME_F8),
new TerminalKey(VT320.VK_F9, VTKEYNAME_F9),
new TerminalKey(VT320.VK_F10, VTKEYNAME_F10),
new TerminalKey(VT320.VK_F11, VTKEYNAME_F11),
new TerminalKey(VT320.VK_F12, VTKEYNAME_F12)
};
/**
* Direction terminal keys. These are also "special" keys in terms of how we send them, but we apply different
* handling in terms of scrollback so we separate them out for usage in a different command.
*/
public static TerminalKey[] directionalTerminalKeys = new TerminalKey[] {
new TerminalKey(VT320.VK_UP, VTKEYNAME_UP),
new TerminalKey(VT320.VK_DOWN, VTKEYNAME_DOWN),
new TerminalKey(VT320.VK_LEFT, VTKEYNAME_LEFT),
new TerminalKey(VT320.VK_RIGHT, VTKEYNAME_RIGHT) };
static IntHashtable terminalKeys;
public static TerminalKey[] getTerminalKeys() {
return specialTerminalKeys;
}
public static TerminalKey[] getDirectionalTerminalKeys() {
return directionalTerminalKeys;
}
// @todo we may also want to support Hold-and-scroll...
public static int SUPPORTED_KEYPAD_MODIFIERS = KeypadListener.STATUS_ALT | KeypadListener.STATUS_SHIFT | KeypadListener.STATUS_SHIFT_LEFT
| KeypadListener.STATUS_SHIFT_RIGHT;
public static String getModifierFriendlyName(int status) {
ResourceBundle res = ResourceBundle.getBundle(BUNDLE_ID, BUNDLE_NAME);
if ((status & KeypadListener.STATUS_ALT) > 0) {
return res.getString(MODNAME_ALT);
}
// ShiftX gets reported as "Left Shift" constnat - but we don't want to confuse
// the user, who will not see a left shift on that keyboard; so we check for this first.
if (PlatformServicesProvider.getInstance().hasShiftX()) {
if ((status & KeypadListener.STATUS_SHIFT_LEFT) > 0 ||
(status & KeypadListener.STATUS_SHIFT) > 0) {
return res.getString(MODNAME_SHIFT);
}
}
if ((status & KeypadListener.STATUS_SHIFT_LEFT) > 0) {
return res.getString(MODNAME_LSHIFT);
}
if ((status & KeypadListener.STATUS_SHIFT_RIGHT) > 0) {
return res.getString(MODNAME_RSHIFT);
}
if ((status & KeypadListener.STATUS_SHIFT) > 0) {
return res.getString(MODNAME_SHIFT);
}
// We don't currently support this for binding modifier, I would like to in the future but it will require
// shifting some of our processing to key-up...
// if ((status & KeypadListener.STATUS_KEY_HELD_WHILE_ROLLING) > 0) {
// return res.getString(MODNAME_KEY_ROLL);
// }
return "";
}
/**
* More of the same mess... serious cleanup needed in here.
*
* @param map
* @param keys
*/
private static void populateLookupMap(TerminalKey[] keys) {
if (terminalKeys == null)
terminalKeys = new IntHashtable(100);
for (int x = keys.length - 1; x >= 0; x--) {
terminalKeys.put(keys[x].getIntValue(), keys[x]);
}
}
public static String getTerminalKeyFriendlyName(int code) {
if (terminalKeys == null) {
populateLookupMap(getTerminalKeys());
populateLookupMap(getDirectionalTerminalKeys());
}
Object o = terminalKeys.get(code);
if (o == null) {
return "Invalid Key";
}
return o.toString();
}
public static int getTerminalKeyIndex(int intValue) {
for (int x = specialTerminalKeys.length - 1; x >= 0; x--) {
if (specialTerminalKeys[x].getIntValue() == intValue) {
return x;
}
}
return -1;
}
public static int getMovementTerminalKeyIndex(int intValue) {
for (int x = directionalTerminalKeys.length - 1; x >= 0; x--) {
if (directionalTerminalKeys[x].getIntValue() == intValue) {
return x;
}
}
return -1;
}
public static Object[] getFontChangeChoices() {
return FONT_CHANGE_CHOICES;
}
}