package juloo.keyboard2; import android.view.KeyCharacterMap; import android.view.KeyEvent; import java.util.HashMap; class KeyModifier { /** Cache key is KeyValue's name */ private static HashMap> _cache = new HashMap>(); /** Represents a removed key, because a cache entry can't be [null]. */ private static final KeyValue removed_key = KeyValue.getKeyByName("removed"); /** Modify a key according to modifiers. */ public static KeyValue modify(KeyValue k, Pointers.Modifiers mods) { if (k == null) return null; int n_mods = mods.size(); HashMap ks = cacheEntry(k); KeyValue r = ks.get(mods); if (r == null) { r = k; /* Order: Fn, Shift, accents */ for (int i = 0; i < n_mods; i++) r = modify(r, mods.get(i)); r = remove_placeholders(r); ks.put(mods, r); } return (r == removed_key) ? null : r; } public static KeyValue modify(KeyValue k, int mod) { switch (mod) { case KeyValue.MOD_CTRL: case KeyValue.MOD_ALT: case KeyValue.MOD_META: return turn_into_keyevent(k); case KeyValue.MOD_FN: return apply_fn(k); case KeyValue.MOD_SHIFT: return apply_shift(k); case KeyValue.MOD_GRAVE: return apply_dead_char(k, '\u02CB'); case KeyValue.MOD_AIGU: return apply_dead_char(k, '\u00B4'); case KeyValue.MOD_CIRCONFLEXE: return apply_dead_char(k, '\u02C6'); case KeyValue.MOD_TILDE: return apply_dead_char(k, '\u02DC'); case KeyValue.MOD_CEDILLE: return apply_dead_char(k, '\u00B8'); case KeyValue.MOD_TREMA: return apply_dead_char(k, '\u00A8'); case KeyValue.MOD_CARON: return apply_dead_char(k, '\u02C7'); case KeyValue.MOD_RING: return apply_dead_char(k, '\u02DA'); case KeyValue.MOD_MACRON: return apply_dead_char(k, '\u00AF'); case KeyValue.MOD_OGONEK: return apply_dead_char(k, '\u02DB'); case KeyValue.MOD_DOT_ABOVE: return apply_dead_char(k, '\u02D9'); case KeyValue.MOD_DOUBLE_AIGU: return apply_map_char(k, map_char_double_aigu); case KeyValue.MOD_ORDINAL: return apply_map_char(k, map_char_ordinal); case KeyValue.MOD_SUPERSCRIPT: return apply_map_char(k, map_char_superscript); case KeyValue.MOD_SUBSCRIPT: return apply_map_char(k, map_char_subscript); case KeyValue.MOD_ARROWS: return apply_map_char(k, map_char_arrows); case KeyValue.MOD_BOX: return apply_map_char(k, map_char_box); case KeyValue.MOD_SLASH: return apply_map_char(k, map_char_slash); case KeyValue.MOD_ARROW_RIGHT: return apply_combining(k, "\u20D7"); default: return k; } } private static KeyValue apply_map_char(KeyValue k, Map_char map) { switch (k.getKind()) { case Char: char kc = k.getChar(); char c = map.apply(kc); return (kc == c) ? k : k.withCharAndSymbol(c); default: return k; } } private static KeyValue apply_dead_char(KeyValue k, char dead_char) { switch (k.getKind()) { case Char: char kc = k.getChar(); char c = (char)KeyCharacterMap.getDeadChar(dead_char, kc); if (c == 0 || kc == c) return k; return k.withCharAndSymbol(c); default: return k; } } private static KeyValue apply_combining(KeyValue k, String combining) { switch (k.getKind()) { case Char: String s = String.valueOf(k.getChar()) + combining; return k.withCharAndSymbol(s, KeyValue.CHAR_NONE); default: return k; } } private static KeyValue apply_shift(KeyValue k) { switch (k.getKind()) { case Char: char kc = k.getChar(); char c = map_char_shift(kc); if (kc == c) c = Character.toUpperCase(kc); if (kc == c) return k; return k.withCharAndSymbol(c); case String: String s = k.getString().toUpperCase(); return k.withCharAndSymbol(s, KeyValue.CHAR_NONE); default: return k; } } private static KeyValue apply_fn(KeyValue k) { String name; switch (k.name) { case "1": name = "f1"; break; case "2": name = "f2"; break; case "3": name = "f3"; break; case "4": name = "f4"; break; case "5": name = "f5"; break; case "6": name = "f6"; break; case "7": name = "f7"; break; case "8": name = "f8"; break; case "9": name = "f9"; break; case "0": name = "f10"; break; case "f11_placeholder": name = "f11"; break; case "f12_placeholder": name = "f12"; break; case "up": name = "page_up"; break; case "down": name = "page_down"; break; case "left": name = "home"; break; case "right": name = "end"; break; case "<": name = "«"; break; case ">": name = "»"; break; case "{": name = "‹"; break; case "}": name = "›"; break; case "[": name = "‘"; break; case "]": name = "’"; break; case "(": name = "“"; break; case ")": name = "”"; break; case "'": name = "‚"; break; case "\"": name = "„"; break; case "-": name = "–"; break; case "_": name = "—"; break; case "^": name = "¬"; break; case "%": name = "‰"; break; case "=": name = "≈"; break; case "u": name = "µ"; break; case "a": name = "æ"; break; case "o": name = "œ"; break; case "esc": name = "insert"; break; case "*": name = "°"; break; case ".": name = "…"; break; case ",": name = "·"; break; case "!": name = "¡"; break; case "?": name = "¿"; break; case "tab": name = "\\t"; break; case "space": name = "nbsp"; break; case "↖": name = "⇖"; break; case "↑": name = "⇑"; break; case "↗": name = "⇗"; break; case "←": name = "⇐"; break; case "→": name = "⇒"; break; case "↙": name = "⇙"; break; case "↓": name = "⇓"; break; case "↘": name = "⇘"; break; // Currency symbols case "e": name = "€"; break; case "l": name = "£"; break; case "r": name = "₹"; break; case "y": name = "¥"; break; case "c": name = "¢"; break; case "p": name = "₱"; break; case "€": case "£": return removed_key; // Avoid showing these twice default: return k; } return KeyValue.getKeyByName(name); } private static KeyValue turn_into_keyevent(KeyValue k) { if (k.getKind() != KeyValue.Kind.Char) return k; int e; switch (k.getChar()) { case 'a': e = KeyEvent.KEYCODE_A; break; case 'b': e = KeyEvent.KEYCODE_B; break; case 'c': e = KeyEvent.KEYCODE_C; break; case 'd': e = KeyEvent.KEYCODE_D; break; case 'e': e = KeyEvent.KEYCODE_E; break; case 'f': e = KeyEvent.KEYCODE_F; break; case 'g': e = KeyEvent.KEYCODE_G; break; case 'h': e = KeyEvent.KEYCODE_H; break; case 'i': e = KeyEvent.KEYCODE_I; break; case 'j': e = KeyEvent.KEYCODE_J; break; case 'k': e = KeyEvent.KEYCODE_K; break; case 'l': e = KeyEvent.KEYCODE_L; break; case 'm': e = KeyEvent.KEYCODE_M; break; case 'n': e = KeyEvent.KEYCODE_N; break; case 'o': e = KeyEvent.KEYCODE_O; break; case 'p': e = KeyEvent.KEYCODE_P; break; case 'q': e = KeyEvent.KEYCODE_Q; break; case 'r': e = KeyEvent.KEYCODE_R; break; case 's': e = KeyEvent.KEYCODE_S; break; case 't': e = KeyEvent.KEYCODE_T; break; case 'u': e = KeyEvent.KEYCODE_U; break; case 'v': e = KeyEvent.KEYCODE_V; break; case 'w': e = KeyEvent.KEYCODE_W; break; case 'x': e = KeyEvent.KEYCODE_X; break; case 'y': e = KeyEvent.KEYCODE_Y; break; case 'z': e = KeyEvent.KEYCODE_Z; break; case '0': e = KeyEvent.KEYCODE_0; break; case '1': e = KeyEvent.KEYCODE_1; break; case '2': e = KeyEvent.KEYCODE_2; break; case '3': e = KeyEvent.KEYCODE_3; break; case '4': e = KeyEvent.KEYCODE_4; break; case '5': e = KeyEvent.KEYCODE_5; break; case '6': e = KeyEvent.KEYCODE_6; break; case '7': e = KeyEvent.KEYCODE_7; break; case '8': e = KeyEvent.KEYCODE_8; break; case '9': e = KeyEvent.KEYCODE_9; break; case '`': e = KeyEvent.KEYCODE_GRAVE; break; case '-': e = KeyEvent.KEYCODE_MINUS; break; case '=': e = KeyEvent.KEYCODE_EQUALS; break; case '[': e = KeyEvent.KEYCODE_LEFT_BRACKET; break; case ']': e = KeyEvent.KEYCODE_RIGHT_BRACKET; break; case '\\': e = KeyEvent.KEYCODE_BACKSLASH; break; case ';': e = KeyEvent.KEYCODE_SEMICOLON; break; case '\'': e = KeyEvent.KEYCODE_APOSTROPHE; break; case '/': e = KeyEvent.KEYCODE_SLASH; break; case '@': e = KeyEvent.KEYCODE_AT; break; case '+': e = KeyEvent.KEYCODE_PLUS; break; case ',': e = KeyEvent.KEYCODE_COMMA; break; case '.': e = KeyEvent.KEYCODE_PERIOD; break; case '*': e = KeyEvent.KEYCODE_STAR; break; case '#': e = KeyEvent.KEYCODE_POUND; break; case '(': e = KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN; break; case ')': e = KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN; break; case ' ': e = KeyEvent.KEYCODE_SPACE; break; default: return k; } return k.withEvent(e); } /** Remove placeholder keys that haven't been modified into something. */ private static KeyValue remove_placeholders(KeyValue k) { switch (k.name) { case "f11_placeholder": case "f12_placeholder": return removed_key; default: return k; } } /* Lookup the cache entry for a key. Create it needed. */ private static HashMap cacheEntry(KeyValue k) { HashMap ks = _cache.get(k.name); if (ks == null) { ks = new HashMap(); _cache.put(k.name, ks); } return ks; } private static abstract class Map_char { public abstract char apply(char c); } private static char map_char_shift(char c) { switch (c) { case '↙': return '⇙'; case '↓': return '⇓'; case '↘': return '⇘'; case '←': return '⇐'; case '→': return '⇒'; case '↖': return '⇖'; case '↑': return '⇑'; case '↗': return '⇗'; case '└': return '╚'; case '┴': return '╩'; case '┘': return '╝'; case '├': return '╠'; case '┼': return '╬'; case '┤': return '╣'; case '┌': return '╔'; case '┬': return '╦'; case '┐': return '╗'; case '─': return '═'; case '│': return '║'; default: return c; } } private static final Map_char map_char_double_aigu = new Map_char() { public char apply(char c) { switch (c) { // Composite characters: a̋ e̋ i̋ m̋ ӳ case 'o': return 'ő'; case 'u': return 'ű'; case ' ': return '˝'; default: return c; } } }; private static final Map_char map_char_ordinal = new Map_char() { public char apply(char c) { switch (c) { case 'a': return 'ª'; case 'o': return 'º'; case '1': return 'ª'; case '2': return 'º'; case '3': return 'ⁿ'; case '4': return 'ᵈ'; case '5': return 'ᵉ'; case '6': return 'ʳ'; case '7': return 'ˢ'; case '8': return 'ᵗ'; case '9': return 'ʰ'; case '*': return '°'; default: return c; } } }; private static final Map_char map_char_superscript = new Map_char() { public char apply(char c) { switch (c) { case '1': return '¹'; case '2': return '²'; case '3': return '³'; case '4': return '⁴'; case '5': return '⁵'; case '6': return '⁶'; case '7': return '⁷'; case '8': return '⁸'; case '9': return '⁹'; case '0': return '⁰'; case 'i': return 'ⁱ'; case '+': return '⁺'; case '-': return '⁻'; case '=': return '⁼'; case '(': return '⁽'; case ')': return '⁾'; case 'n': return 'ⁿ'; default: return c; } } }; private static final Map_char map_char_subscript = new Map_char() { public char apply(char c) { switch (c) { case '1': return '₁'; case '2': return '₂'; case '3': return '₃'; case '4': return '₄'; case '5': return '₅'; case '6': return '₆'; case '7': return '₇'; case '8': return '₈'; case '9': return '₉'; case '0': return '₀'; case '+': return '₊'; case '-': return '₋'; case '=': return '₌'; case '(': return '₍'; case ')': return '₎'; case 'e': return 'ₑ'; case 'a': return 'ₐ'; case 'x': return 'ₓ'; case 'o': return 'ₒ'; default: return c; } } }; private static final Map_char map_char_arrows = new Map_char() { public char apply(char c) { switch (c) { case '1': return '↙'; case '2': return '↓'; case '3': return '↘'; case '4': return '←'; case '6': return '→'; case '7': return '↖'; case '8': return '↑'; case '9': return '↗'; default: return c; } } }; private static final Map_char map_char_box = new Map_char() { public char apply(char c) { switch (c) { case '1': return '└'; case '2': return '┴'; case '3': return '┘'; case '4': return '├'; case '5': return '┼'; case '6': return '┤'; case '7': return '┌'; case '8': return '┬'; case '9': return '┐'; case '0': return '─'; case '.': return '│'; default: return c; } } }; private static final Map_char map_char_slash = new Map_char() { public char apply(char c) { switch (c) { case 'a': return 'ⱥ'; case 'c': return 'ȼ'; case 'e': return 'ɇ'; case 'g': return 'ꞡ'; case 'l': return 'ł'; case 'n': return 'ꞥ'; case 'o': return 'ø'; case ' ': return '/'; default: return c; } } }; }