/*
* Copyright (C) 2015-present, osfans
* waxaca@163.com https://github.com/osfans
*
* 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 3 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, see .
*/
package com.osfans.trime;
import android.view.KeyEvent;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/** {@link Key 按鍵}的各種事件(單擊、長按、滑動等) */
public class Event {
private String TAG = "Event";
private Keyboard mKeyboard;
private int code = 0;
private int mask = 0;
private String text;
private String label;
private String preview;
private List states;
private String command;
private String option;
private String select;
private String toggle;
private String commit;
private String shiftLock;
private boolean functional;
private boolean repeatable;
private boolean sticky;
public Event(Keyboard keyboard, String s) {
mKeyboard = keyboard;
if (s.matches("\\{[^\\{\\}]+\\}")) { //{send|key}
label = s.substring(1, s.length() -1);
int[] sends = parseSend(label); //send
code = sends[0];
mask = sends[1];
if (code >= 0) return;
s = label; //key
label = null;
}
if (Key.presetKeys.containsKey(s)) {
Map m = Key.presetKeys.get(s);
command = Config.getString(m, "command");
option = Config.getString(m, "option");
select = Config.getString(m, "select");
toggle = Config.getString(m, "toggle");
label = Config.getString(m, "label");
preview = Config.getString(m, "preview");
shiftLock = Config.getString(m, "shift_lock");
commit = Config.getString(m, "commit");
String send = Config.getString(m, "send");
if (Function.isEmpty(send) && !Function.isEmpty(command)) send = "function"; //command默認發function
int[] sends = parseSend(send);
code = sends[0];
mask = sends[1];
parseLabel();
text = Config.getString(m, "text");
if (code < 0 && Function.isEmpty(text)) text = s;
if (m.containsKey("states")) states = (List) m.get("states");
sticky = Config.getBoolean(m, "sticky", false);
repeatable = Config.getBoolean(m, "repeatable", false);
functional = Config.getBoolean(m, "functional", true);
} else if ((code = getClickCode(s)) >= 0) {
parseLabel();
} else {
code = 0;
text = s;
label = s.replaceAll("\\{[^\\{\\}]+?\\}", "");
}
}
public Event(String s) {
this(null, s);
}
public int getCode() {
return code;
}
public int getMask() {
return mask;
}
public String getCommand() {
return command;
}
public String getOption() {
return option;
}
public String getSelect() {
return select;
}
public boolean isFunctional() {
return functional;
}
public boolean isRepeatable() {
return repeatable;
}
public boolean isSticky() {
return sticky;
}
public String getShiftLock() {
return shiftLock;
}
public static int[] parseSend(String s) {
int[] sends = new int[2];
if (Function.isEmpty(s)) return sends;
String codes;
if (!s.contains("+")) codes = s;
else {
String[] ss = s.split("\\+");
int n = ss.length;
for (int i = 0; i < n - 1; i++) if (masks.containsKey(ss[i])) sends[1] |= masks.get(ss[i]);
codes = ss[n - 1];
}
sends[0] = Key.androidKeys.indexOf(codes);
return sends;
}
private String adjustCase(String s) {
if (Function.isEmpty(s)) return "";
if (s.length() == 1 && mKeyboard != null && mKeyboard.isShifted())
s = s.toUpperCase(Locale.getDefault());
else if (s.length() == 1
&& mKeyboard != null
&& !Rime.isAsciiMode()
&& mKeyboard.isLabelUppercase()) s = s.toUpperCase(Locale.getDefault());
return s;
}
public String getLabel() {
if (!Function.isEmpty(toggle)) return states.get(Rime.getOption(toggle) ? 1 : 0);
return adjustCase(label);
}
public String getText() {
String s = "";
if (!Function.isEmpty(text)) s = text;
else if (mKeyboard != null
&& mKeyboard.isShifted()
&& mask == 0
&& code >= KeyEvent.KEYCODE_A
&& code <= KeyEvent.KEYCODE_Z) s = label;
return adjustCase(s);
}
public String getPreviewText() {
if (!Function.isEmpty(preview)) return preview;
return getLabel();
}
public String getToggle() {
if (!Function.isEmpty(toggle)) return toggle;
return "ascii_mode";
}
public String getCommit() {
return commit;
}
private void parseLabel() {
if (!Function.isEmpty(label)) return;
int c = code;
if (c == KeyEvent.KEYCODE_SPACE) {
label = Rime.getSchemaName();
} else {
if (c > 0) label = getDisplayLabel(c);
}
}
public static String getDisplayLabel(int keyCode) {
String s = "";
if (keyCode < Key.getSymbolStart()) { //字母數字
if (Key.getKcm().isPrintingKey(keyCode)) {
char c = Key.getKcm().getDisplayLabel(keyCode);
if (Character.isUpperCase(c)) c = Character.toLowerCase(c);
s = String.valueOf(c);
} else {
s = Key.androidKeys.get(keyCode);
}
} else if (keyCode < Key.androidKeys.size()) { //可見符號
keyCode -= Key.getSymbolStart();
s = Key.getSymbols().substring(keyCode, keyCode + 1);
}
return s;
}
public static int getClickCode(String s) {
int keyCode = -1;
if (Function.isEmpty(s)) { //空鍵
keyCode = 0;
} else if (Key.androidKeys.contains(s)) { //字母數字
keyCode = Key.androidKeys.indexOf(s);
} else if (Key.getSymbols().contains(s)) { //可見符號
keyCode = Key.getSymbolStart() + Key.getSymbols().indexOf(s);
} else if (symbolAliases.containsKey(s)) {
keyCode = symbolAliases.get(s);
}
return keyCode;
}
private static int getRimeCode(int code) {
int i = 0;
if (code >= 0 && code < Key.androidKeys.size()) {
String s = Key.androidKeys.get(code);
i = Rime.get_keycode_by_name(s);
}
return i;
}
public static boolean hasModifier(int mask, int modifier) {
return (mask & modifier) > 0;
}
public static int[] getRimeEvent(int code, int mask) {
int i = getRimeCode(code);
int m = 0;
if (hasModifier(mask, KeyEvent.META_SHIFT_ON)) m |= Rime.META_SHIFT_ON;
if (hasModifier(mask, KeyEvent.META_CTRL_ON)) m |= Rime.META_CTRL_ON;
if (hasModifier(mask, KeyEvent.META_ALT_ON)) m |= Rime.META_ALT_ON;
if (mask == Rime.META_RELEASE_ON) m |= Rime.META_RELEASE_ON;
return new int[] {i, m};
}
private static Map masks =
new HashMap() {
{
put("Shift", KeyEvent.META_SHIFT_ON);
put("Control", KeyEvent.META_CTRL_ON);
put("Alt", KeyEvent.META_ALT_ON);
}
};
private static Map symbolAliases =
new HashMap() {
{
put("#", KeyEvent.KEYCODE_POUND);
put("'", KeyEvent.KEYCODE_APOSTROPHE);
put("(", KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN);
put(")", KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN);
put("*", KeyEvent.KEYCODE_STAR);
put("+", KeyEvent.KEYCODE_PLUS);
put(",", KeyEvent.KEYCODE_COMMA);
put("-", KeyEvent.KEYCODE_MINUS);
put(".", KeyEvent.KEYCODE_PERIOD);
put("/", KeyEvent.KEYCODE_SLASH);
put(";", KeyEvent.KEYCODE_SEMICOLON);
put("=", KeyEvent.KEYCODE_EQUALS);
put("@", KeyEvent.KEYCODE_AT);
put("\\", KeyEvent.KEYCODE_BACKSLASH);
put("[", KeyEvent.KEYCODE_LEFT_BRACKET);
put("`", KeyEvent.KEYCODE_GRAVE);
put("]", KeyEvent.KEYCODE_RIGHT_BRACKET);
}
};
}