2021-04-18 23:28:49 +02:00
|
|
|
|
package juloo.keyboard2;
|
|
|
|
|
|
|
|
|
|
import android.view.KeyCharacterMap;
|
2021-04-18 23:58:35 +02:00
|
|
|
|
import android.view.KeyEvent;
|
2021-04-18 23:28:49 +02:00
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
2024-01-13 20:59:05 +01:00
|
|
|
|
public final class KeyModifier
|
2021-04-18 23:28:49 +02:00
|
|
|
|
{
|
2024-04-01 22:57:53 +02:00
|
|
|
|
/** The optional modmap takes priority over modifiers usual behaviors. Set to
|
|
|
|
|
[null] to disable. */
|
|
|
|
|
private static KeyboardData.Modmap _modmap = null;
|
2024-05-02 11:58:44 +02:00
|
|
|
|
public static void set_modmap(KeyboardData.Modmap mm)
|
|
|
|
|
{
|
|
|
|
|
_modmap = mm;
|
|
|
|
|
}
|
2024-04-01 22:57:53 +02:00
|
|
|
|
|
2022-03-19 16:27:57 +01:00
|
|
|
|
/** Modify a key according to modifiers. */
|
2022-06-05 01:38:42 +02:00
|
|
|
|
public static KeyValue modify(KeyValue k, Pointers.Modifiers mods)
|
2021-04-18 23:28:49 +02:00
|
|
|
|
{
|
2022-03-19 16:27:57 +01:00
|
|
|
|
if (k == null)
|
|
|
|
|
return null;
|
2022-06-05 01:38:42 +02:00
|
|
|
|
int n_mods = mods.size();
|
2024-06-09 12:49:23 +02:00
|
|
|
|
KeyValue r = k;
|
|
|
|
|
for (int i = 0; i < n_mods; i++)
|
|
|
|
|
r = modify(r, mods.get(i));
|
2022-06-06 00:23:45 +02:00
|
|
|
|
/* Keys with an empty string are placeholder keys. */
|
2024-02-11 20:46:36 +01:00
|
|
|
|
if (r.getString().length() == 0)
|
|
|
|
|
return null;
|
|
|
|
|
return r;
|
2021-04-18 23:28:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-18 00:14:19 +01:00
|
|
|
|
public static KeyValue modify(KeyValue k, KeyValue mod)
|
|
|
|
|
{
|
|
|
|
|
switch (mod.getKind())
|
|
|
|
|
{
|
|
|
|
|
case Modifier:
|
|
|
|
|
return modify(k, mod.getModifier());
|
2024-03-18 01:00:22 +01:00
|
|
|
|
case Compose_pending:
|
|
|
|
|
return ComposeKey.apply(mod.getPendingCompose(), k);
|
2024-05-02 20:52:18 +02:00
|
|
|
|
case Hangul_initial:
|
|
|
|
|
if (k.equals(mod)) // Allow typing the initial in letter form
|
|
|
|
|
return KeyValue.makeStringKey(k.getString(), KeyValue.FLAG_GREYED);
|
|
|
|
|
return combine_hangul_initial(k, mod.getHangulPrecomposed());
|
|
|
|
|
case Hangul_medial:
|
|
|
|
|
return combine_hangul_medial(k, mod.getHangulPrecomposed());
|
2024-03-18 00:14:19 +01:00
|
|
|
|
}
|
|
|
|
|
return k;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-05 19:30:53 +02:00
|
|
|
|
public static KeyValue modify(KeyValue k, KeyValue.Modifier mod)
|
2021-04-18 23:28:49 +02:00
|
|
|
|
{
|
2022-06-05 01:38:42 +02:00
|
|
|
|
switch (mod)
|
|
|
|
|
{
|
2024-07-25 23:40:58 +02:00
|
|
|
|
case CTRL: return apply_ctrl(k);
|
2022-06-05 19:30:53 +02:00
|
|
|
|
case ALT:
|
|
|
|
|
case META: return turn_into_keyevent(k);
|
|
|
|
|
case FN: return apply_fn(k);
|
2024-05-25 21:19:44 +02:00
|
|
|
|
case GESTURE: return apply_gesture(k);
|
2022-06-05 19:30:53 +02:00
|
|
|
|
case SHIFT: return apply_shift(k);
|
2024-06-09 11:43:21 +02:00
|
|
|
|
case GRAVE: return apply_compose_or_dead_char(k, ComposeKeyData.accent_grave, '\u02CB');
|
|
|
|
|
case AIGU: return apply_compose_or_dead_char(k, ComposeKeyData.accent_aigu, '\u00B4');
|
|
|
|
|
case CIRCONFLEXE: return apply_compose_or_dead_char(k, ComposeKeyData.accent_circonflexe, '\u02C6');
|
|
|
|
|
case TILDE: return apply_compose_or_dead_char(k, ComposeKeyData.accent_tilde, '\u02DC');
|
|
|
|
|
case CEDILLE: return apply_compose_or_dead_char(k, ComposeKeyData.accent_cedille, '\u00B8');
|
|
|
|
|
case TREMA: return apply_compose_or_dead_char(k, ComposeKeyData.accent_trema, '\u00A8');
|
|
|
|
|
case CARON: return apply_compose_or_dead_char(k, ComposeKeyData.accent_caron, '\u02C7');
|
|
|
|
|
case RING: return apply_compose_or_dead_char(k, ComposeKeyData.accent_ring, '\u02DA');
|
|
|
|
|
case MACRON: return apply_compose_or_dead_char(k, ComposeKeyData.accent_macron, '\u00AF');
|
|
|
|
|
case OGONEK: return apply_compose_or_dead_char(k, ComposeKeyData.accent_ogonek, '\u02DB');
|
|
|
|
|
case DOT_ABOVE: return apply_compose_or_dead_char(k, ComposeKeyData.accent_dot_above, '\u02D9');
|
|
|
|
|
case BREVE: return apply_dead_char(k, '\u02D8');
|
|
|
|
|
case DOUBLE_AIGU: return apply_compose(k, ComposeKeyData.accent_double_aigu);
|
|
|
|
|
case ORDINAL: return apply_compose(k, ComposeKeyData.accent_ordinal);
|
|
|
|
|
case SUPERSCRIPT: return apply_compose(k, ComposeKeyData.accent_superscript);
|
|
|
|
|
case SUBSCRIPT: return apply_compose(k, ComposeKeyData.accent_subscript);
|
|
|
|
|
case ARROWS: return apply_compose(k, ComposeKeyData.accent_arrows);
|
|
|
|
|
case BOX: return apply_compose(k, ComposeKeyData.accent_box);
|
|
|
|
|
case SLASH: return apply_compose(k, ComposeKeyData.accent_slash);
|
|
|
|
|
case BAR: return apply_compose(k, ComposeKeyData.accent_bar);
|
|
|
|
|
case DOT_BELOW: return apply_compose(k, ComposeKeyData.accent_dot_below);
|
|
|
|
|
case HORN: return apply_compose(k, ComposeKeyData.accent_horn);
|
|
|
|
|
case HOOK_ABOVE: return apply_compose(k, ComposeKeyData.accent_hook_above);
|
2023-08-20 01:01:38 +02:00
|
|
|
|
case ARROW_RIGHT: return apply_map_char(k, map_char_arrow_right);
|
2022-06-05 01:38:42 +02:00
|
|
|
|
default: return k;
|
|
|
|
|
}
|
2021-05-08 02:00:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-12 23:14:57 +01:00
|
|
|
|
/** Modify a key after a long press. */
|
|
|
|
|
public static KeyValue modify_long_press(KeyValue k)
|
|
|
|
|
{
|
2023-02-12 23:20:11 +01:00
|
|
|
|
switch (k.getKind())
|
|
|
|
|
{
|
|
|
|
|
case Event:
|
|
|
|
|
switch (k.getEvent())
|
|
|
|
|
{
|
2024-01-15 22:09:15 +01:00
|
|
|
|
case CHANGE_METHOD_AUTO:
|
2023-02-12 23:20:11 +01:00
|
|
|
|
return KeyValue.getKeyByName("change_method");
|
2023-12-30 01:24:21 +01:00
|
|
|
|
case SWITCH_VOICE_TYPING:
|
|
|
|
|
return KeyValue.getKeyByName("voice_typing_chooser");
|
2023-02-12 23:20:11 +01:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-02-12 23:14:57 +01:00
|
|
|
|
return k;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-26 17:05:51 +01:00
|
|
|
|
public static Map_char modify_numpad_script(String numpad_script)
|
2023-09-03 23:38:55 +02:00
|
|
|
|
{
|
2023-12-26 17:05:51 +01:00
|
|
|
|
if (numpad_script == null)
|
2023-09-03 23:38:55 +02:00
|
|
|
|
return map_char_none;
|
2023-12-26 17:05:51 +01:00
|
|
|
|
switch (numpad_script)
|
2023-09-03 23:38:55 +02:00
|
|
|
|
{
|
2023-12-26 17:16:29 +01:00
|
|
|
|
case "hindu-arabic": return map_char_numpad_hindu;
|
2023-09-03 23:38:55 +02:00
|
|
|
|
case "bengali": return map_char_numpad_bengali;
|
|
|
|
|
case "devanagari": return map_char_numpad_devanagari;
|
|
|
|
|
case "persian": return map_char_numpad_persian;
|
2024-03-03 23:12:52 +01:00
|
|
|
|
case "gujarati": return map_char_numpad_gujarati;
|
2023-09-03 23:38:55 +02:00
|
|
|
|
default: return map_char_none;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-05 17:26:34 +02:00
|
|
|
|
private static KeyValue apply_map_char(KeyValue k, Map_char map)
|
|
|
|
|
{
|
|
|
|
|
switch (k.getKind())
|
|
|
|
|
{
|
|
|
|
|
case Char:
|
|
|
|
|
char kc = k.getChar();
|
2023-08-20 00:44:22 +02:00
|
|
|
|
String modified = map.apply(kc);
|
2024-05-25 21:19:44 +02:00
|
|
|
|
if (modified != null)
|
|
|
|
|
return KeyValue.makeStringKey(modified, k.getFlags());
|
2022-06-05 17:26:34 +02:00
|
|
|
|
}
|
2024-05-25 21:19:44 +02:00
|
|
|
|
return k;
|
2022-06-05 17:26:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 11:43:21 +02:00
|
|
|
|
/** Apply the given compose state or fallback to the dead_char. */
|
|
|
|
|
private static KeyValue apply_compose_or_dead_char(KeyValue k, int state, char dead_char)
|
|
|
|
|
{
|
|
|
|
|
switch (k.getKind())
|
|
|
|
|
{
|
|
|
|
|
case Char:
|
|
|
|
|
char c = k.getChar();
|
|
|
|
|
KeyValue r = ComposeKey.apply(state, c);
|
|
|
|
|
if (r != null)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
return apply_dead_char(k, dead_char);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static KeyValue apply_compose(KeyValue k, int state)
|
|
|
|
|
{
|
|
|
|
|
switch (k.getKind())
|
|
|
|
|
{
|
|
|
|
|
case Char:
|
|
|
|
|
KeyValue r = ComposeKey.apply(state, k.getChar());
|
|
|
|
|
if (r != null)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
return k;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static KeyValue apply_dead_char(KeyValue k, char dead_char)
|
|
|
|
|
{
|
|
|
|
|
switch (k.getKind())
|
|
|
|
|
{
|
|
|
|
|
case Char:
|
|
|
|
|
char c = k.getChar();
|
|
|
|
|
char modified = (char)KeyCharacterMap.getDeadChar(dead_char, c);
|
|
|
|
|
if (modified != 0 && modified != c)
|
|
|
|
|
return KeyValue.makeStringKey(String.valueOf(modified));
|
|
|
|
|
}
|
|
|
|
|
return k;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-05 01:38:42 +02:00
|
|
|
|
private static KeyValue apply_shift(KeyValue k)
|
2021-04-18 23:28:49 +02:00
|
|
|
|
{
|
2024-04-01 22:57:53 +02:00
|
|
|
|
if (_modmap != null)
|
|
|
|
|
{
|
|
|
|
|
KeyValue mapped = _modmap.shift.get(k);
|
|
|
|
|
if (mapped != null)
|
|
|
|
|
return mapped;
|
|
|
|
|
}
|
2022-06-05 17:26:34 +02:00
|
|
|
|
switch (k.getKind())
|
2022-06-05 13:51:09 +02:00
|
|
|
|
{
|
2022-06-05 17:26:34 +02:00
|
|
|
|
case Char:
|
|
|
|
|
char kc = k.getChar();
|
|
|
|
|
char c = map_char_shift(kc);
|
|
|
|
|
if (kc == c)
|
|
|
|
|
c = Character.toUpperCase(kc);
|
2022-06-05 18:14:50 +02:00
|
|
|
|
return (kc == c) ? k : k.withChar(c);
|
2022-06-05 17:26:34 +02:00
|
|
|
|
case String:
|
2023-09-09 14:32:03 +02:00
|
|
|
|
String s = Utils.capitalize_string(k.getString());
|
|
|
|
|
return KeyValue.makeStringKey(s, k.getFlags());
|
2022-06-05 17:26:34 +02:00
|
|
|
|
default: return k;
|
2022-06-05 13:51:09 +02:00
|
|
|
|
}
|
2021-04-18 23:28:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-05 01:38:42 +02:00
|
|
|
|
private static KeyValue apply_fn(KeyValue k)
|
2021-04-18 23:58:35 +02:00
|
|
|
|
{
|
2024-05-02 12:09:39 +02:00
|
|
|
|
if (_modmap != null)
|
|
|
|
|
{
|
|
|
|
|
KeyValue mapped = _modmap.fn.get(k);
|
|
|
|
|
if (mapped != null)
|
|
|
|
|
return mapped;
|
|
|
|
|
}
|
2022-06-06 00:23:45 +02:00
|
|
|
|
String name = null;
|
|
|
|
|
switch (k.getKind())
|
2021-04-18 23:58:35 +02:00
|
|
|
|
{
|
2024-09-10 22:43:40 +02:00
|
|
|
|
case Char:
|
|
|
|
|
KeyValue r = ComposeKey.apply(ComposeKeyData.fn, k.getChar());
|
|
|
|
|
return (r != null) ? r : k;
|
2022-06-06 00:23:45 +02:00
|
|
|
|
case Keyevent: name = apply_fn_keyevent(k.getKeyevent()); break;
|
2022-07-09 17:31:44 +02:00
|
|
|
|
case Event: name = apply_fn_event(k.getEvent()); break;
|
2023-01-30 21:29:59 +01:00
|
|
|
|
case Placeholder: name = apply_fn_placeholder(k.getPlaceholder()); break;
|
2024-07-28 21:29:50 +02:00
|
|
|
|
case Cursor_move: name = apply_fn_cursormove(k.getCursorMove()); break;
|
2022-06-06 00:23:45 +02:00
|
|
|
|
}
|
|
|
|
|
return (name == null) ? k : KeyValue.getKeyByName(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static String apply_fn_keyevent(int code)
|
|
|
|
|
{
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_UP: return "page_up";
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_DOWN: return "page_down";
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_LEFT: return "home";
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_RIGHT: return "end";
|
|
|
|
|
case KeyEvent.KEYCODE_ESCAPE: return "insert";
|
2022-10-16 00:04:06 +02:00
|
|
|
|
case KeyEvent.KEYCODE_TAB: return "\\t";
|
2023-10-20 12:15:50 +02:00
|
|
|
|
case KeyEvent.KEYCODE_PAGE_UP:
|
|
|
|
|
case KeyEvent.KEYCODE_PAGE_DOWN:
|
|
|
|
|
case KeyEvent.KEYCODE_MOVE_HOME:
|
|
|
|
|
case KeyEvent.KEYCODE_MOVE_END: return "removed";
|
2022-06-06 00:23:45 +02:00
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-09 17:31:44 +02:00
|
|
|
|
private static String apply_fn_event(KeyValue.Event ev)
|
|
|
|
|
{
|
|
|
|
|
switch (ev)
|
|
|
|
|
{
|
|
|
|
|
case SWITCH_NUMERIC: return "switch_greekmath";
|
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 21:29:59 +01:00
|
|
|
|
private static String apply_fn_placeholder(KeyValue.Placeholder p)
|
|
|
|
|
{
|
|
|
|
|
switch (p)
|
|
|
|
|
{
|
|
|
|
|
case F11: return "f11";
|
|
|
|
|
case F12: return "f12";
|
2023-01-20 03:58:40 +01:00
|
|
|
|
case SHINDOT: return "shindot";
|
|
|
|
|
case SINDOT: return "sindot";
|
|
|
|
|
case OLE: return "ole";
|
|
|
|
|
case METEG: return "meteg";
|
2023-01-30 21:29:59 +01:00
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-28 21:29:50 +02:00
|
|
|
|
private static String apply_fn_cursormove(short cur)
|
|
|
|
|
{
|
|
|
|
|
switch (cur)
|
|
|
|
|
{
|
|
|
|
|
case -1 : return "home"; // cursor_left
|
|
|
|
|
case 1 : return "end"; // cursor_right
|
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 23:40:58 +02:00
|
|
|
|
private static KeyValue apply_ctrl(KeyValue k)
|
|
|
|
|
{
|
|
|
|
|
if (_modmap != null)
|
|
|
|
|
{
|
|
|
|
|
KeyValue mapped = _modmap.ctrl.get(k);
|
|
|
|
|
// Do not return the modified character right away, first turn it into a
|
|
|
|
|
// key event.
|
|
|
|
|
if (mapped != null)
|
|
|
|
|
k = mapped;
|
|
|
|
|
}
|
|
|
|
|
return turn_into_keyevent(k);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-05 17:44:00 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2022-06-05 19:30:53 +02:00
|
|
|
|
return k.withKeyevent(e);
|
2022-06-05 17:44:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-25 21:19:44 +02:00
|
|
|
|
/** Modify a key affected by a round-trip or a clockwise circle gesture. */
|
|
|
|
|
private static KeyValue apply_gesture(KeyValue k)
|
|
|
|
|
{
|
|
|
|
|
KeyValue shifted = apply_shift(k);
|
|
|
|
|
if (shifted == null || shifted.equals(k))
|
|
|
|
|
return apply_fn(k);
|
|
|
|
|
return shifted;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-03 23:38:55 +02:00
|
|
|
|
public static abstract class Map_char
|
2022-06-05 17:26:34 +02:00
|
|
|
|
{
|
2023-08-20 00:44:22 +02:00
|
|
|
|
/** Modify a char or return [null] if the modifier do not apply. Return a
|
|
|
|
|
[String] that can contains combining diacritics. */
|
|
|
|
|
public abstract String apply(char c);
|
2022-06-05 17:26:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-03 23:38:55 +02:00
|
|
|
|
private static final Map_char map_char_none =
|
|
|
|
|
new Map_char() {
|
|
|
|
|
public String apply(char _c) { return null; }
|
|
|
|
|
};
|
|
|
|
|
|
2022-06-05 01:38:42 +02:00
|
|
|
|
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 '║';
|
2022-07-08 03:04:10 +02:00
|
|
|
|
case 'ß': return 'ẞ';
|
2022-10-04 10:10:54 +02:00
|
|
|
|
/* In Turkish, upper case of 'iı' is 'İI' but Java's toUpperCase will
|
|
|
|
|
return 'II'. To make 'İ' accessible, make it the shift of 'ı'. This
|
|
|
|
|
has the inconvenient of swapping i and ı on the keyboard. */
|
|
|
|
|
case 'ı': return 'İ';
|
2022-11-01 21:44:49 +01:00
|
|
|
|
case '₹': return '₨';
|
2024-03-03 23:12:52 +01:00
|
|
|
|
// Gujarati alternate characters
|
|
|
|
|
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 'ફ';
|
|
|
|
|
case 'બ': return 'ભ';
|
|
|
|
|
case 'મ': return 'ં';
|
|
|
|
|
case 'લ': return 'ળ';
|
|
|
|
|
case 'સ': return 'શ';
|
|
|
|
|
case 'હ': return 'ઃ';
|
2022-06-05 01:38:42 +02:00
|
|
|
|
default: return c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-20 01:01:38 +02:00
|
|
|
|
private static final Map_char map_char_arrow_right =
|
|
|
|
|
new Map_char() {
|
|
|
|
|
public String apply(char c)
|
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
default: return c + "\u20D7";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-25 13:24:52 +02:00
|
|
|
|
// Used with Arabic despite the name; called "Hindi numerals" in Arabic
|
|
|
|
|
// map_char_numpad_devanagari is used in Hindi
|
2023-12-26 17:16:29 +01:00
|
|
|
|
private static final Map_char map_char_numpad_hindu =
|
2023-09-03 23:38:55 +02:00
|
|
|
|
new Map_char() {
|
|
|
|
|
public String apply(char c)
|
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case '0': 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 "٩";
|
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private static final Map_char map_char_numpad_bengali =
|
|
|
|
|
new Map_char() {
|
|
|
|
|
public String apply(char c)
|
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case '0': 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 "৯";
|
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private static final Map_char map_char_numpad_devanagari =
|
|
|
|
|
new Map_char() {
|
|
|
|
|
public String apply(char c)
|
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case '0': 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 "९";
|
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private static final Map_char map_char_numpad_persian =
|
|
|
|
|
new Map_char() {
|
|
|
|
|
public String apply(char c)
|
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case '0': 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 "۹";
|
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-03-03 23:12:52 +01:00
|
|
|
|
|
|
|
|
|
private static final Map_char map_char_numpad_gujarati =
|
|
|
|
|
new Map_char() {
|
|
|
|
|
public String apply(char c)
|
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case '0': 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 "૯";
|
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-05-02 20:52:18 +02:00
|
|
|
|
|
|
|
|
|
/** Compose the precomposed initial with the medial [kv]. */
|
|
|
|
|
private static KeyValue combine_hangul_initial(KeyValue kv, int precomposed)
|
|
|
|
|
{
|
|
|
|
|
switch (kv.getKind())
|
|
|
|
|
{
|
|
|
|
|
case Char:
|
|
|
|
|
return combine_hangul_initial(kv, kv.getChar(), precomposed);
|
|
|
|
|
case Hangul_initial:
|
|
|
|
|
// No initials are expected to compose, grey out
|
|
|
|
|
return kv.withFlags(kv.getFlags() | KeyValue.FLAG_GREYED);
|
|
|
|
|
default:
|
|
|
|
|
return kv;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static KeyValue combine_hangul_initial(KeyValue kv, char medial,
|
|
|
|
|
int precomposed)
|
|
|
|
|
{
|
|
|
|
|
int medial_idx;
|
|
|
|
|
switch (medial)
|
|
|
|
|
{
|
|
|
|
|
// Vowels
|
|
|
|
|
case 'ㅏ': medial_idx = 0; break;
|
|
|
|
|
case 'ㅐ': medial_idx = 1; break;
|
|
|
|
|
case 'ㅑ': medial_idx = 2; break;
|
|
|
|
|
case 'ㅒ': medial_idx = 3; break;
|
|
|
|
|
case 'ㅓ': medial_idx = 4; break;
|
|
|
|
|
case 'ㅔ': medial_idx = 5; break;
|
|
|
|
|
case 'ㅕ': medial_idx = 6; break;
|
|
|
|
|
case 'ㅖ': medial_idx = 7; break;
|
|
|
|
|
case 'ㅗ': medial_idx = 8; break;
|
|
|
|
|
case 'ㅘ': medial_idx = 9; break;
|
|
|
|
|
case 'ㅙ': medial_idx = 10; break;
|
|
|
|
|
case 'ㅚ': medial_idx = 11; break;
|
|
|
|
|
case 'ㅛ': medial_idx = 12; break;
|
|
|
|
|
case 'ㅜ': medial_idx = 13; break;
|
|
|
|
|
case 'ㅝ': medial_idx = 14; break;
|
|
|
|
|
case 'ㅞ': medial_idx = 15; break;
|
|
|
|
|
case 'ㅟ': medial_idx = 16; break;
|
|
|
|
|
case 'ㅠ': medial_idx = 17; break;
|
|
|
|
|
case 'ㅡ': medial_idx = 18; break;
|
|
|
|
|
case 'ㅢ': medial_idx = 19; break;
|
|
|
|
|
case 'ㅣ': medial_idx = 20; break;
|
|
|
|
|
// Grey-out uncomposable characters
|
|
|
|
|
default: return kv.withFlags(kv.getFlags() | KeyValue.FLAG_GREYED);
|
|
|
|
|
}
|
|
|
|
|
return KeyValue.makeHangulMedial(precomposed, medial_idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Combine the precomposed medial with the final [kv]. */
|
|
|
|
|
private static KeyValue combine_hangul_medial(KeyValue kv, int precomposed)
|
|
|
|
|
{
|
|
|
|
|
switch (kv.getKind())
|
|
|
|
|
{
|
|
|
|
|
case Char:
|
|
|
|
|
return combine_hangul_medial(kv, kv.getChar(), precomposed);
|
|
|
|
|
case Hangul_initial:
|
|
|
|
|
// Finals that can also be initials have this kind.
|
|
|
|
|
return combine_hangul_medial(kv, kv.getString().charAt(0), precomposed);
|
|
|
|
|
default:
|
|
|
|
|
return kv;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static KeyValue combine_hangul_medial(KeyValue kv, char c,
|
|
|
|
|
int precomposed)
|
|
|
|
|
{
|
|
|
|
|
int final_idx;
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case ' ': final_idx = 0; break;
|
|
|
|
|
case 'ㄱ': final_idx = 1; break;
|
|
|
|
|
case 'ㄲ': final_idx = 2; break;
|
|
|
|
|
case 'ㄳ': final_idx = 3; break;
|
|
|
|
|
case 'ㄴ': final_idx = 4; break;
|
|
|
|
|
case 'ㄵ': final_idx = 5; break;
|
|
|
|
|
case 'ㄶ': final_idx = 6; break;
|
|
|
|
|
case 'ㄷ': final_idx = 7; break;
|
|
|
|
|
case 'ㄹ': final_idx = 8; break;
|
|
|
|
|
case 'ㄺ': final_idx = 9; break;
|
|
|
|
|
case 'ㄻ': final_idx = 10; break;
|
|
|
|
|
case 'ㄼ': final_idx = 11; break;
|
|
|
|
|
case 'ㄽ': final_idx = 12; break;
|
|
|
|
|
case 'ㄾ': final_idx = 13; break;
|
|
|
|
|
case 'ㄿ': final_idx = 14; break;
|
|
|
|
|
case 'ㅀ': final_idx = 15; break;
|
|
|
|
|
case 'ㅁ': final_idx = 16; break;
|
|
|
|
|
case 'ㅂ': final_idx = 17; break;
|
|
|
|
|
case 'ㅄ': final_idx = 18; break;
|
|
|
|
|
case 'ㅅ': final_idx = 19; break;
|
|
|
|
|
case 'ㅆ': final_idx = 20; break;
|
|
|
|
|
case 'ㅇ': final_idx = 21; break;
|
|
|
|
|
case 'ㅈ': final_idx = 22; break;
|
|
|
|
|
case 'ㅊ': final_idx = 23; break;
|
|
|
|
|
case 'ㅋ': final_idx = 24; break;
|
|
|
|
|
case 'ㅌ': final_idx = 25; break;
|
|
|
|
|
case 'ㅍ': final_idx = 26; break;
|
|
|
|
|
case 'ㅎ': final_idx = 27; break;
|
|
|
|
|
// Grey-out uncomposable characters
|
|
|
|
|
default: return kv.withFlags(kv.getFlags() | KeyValue.FLAG_GREYED);
|
|
|
|
|
}
|
|
|
|
|
return KeyValue.makeHangulFinal(precomposed, final_idx);
|
|
|
|
|
}
|
2021-04-18 23:28:49 +02:00
|
|
|
|
}
|