package org.geogebra.keyboard.web;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import org.geogebra.common.util.lang.Language;
import org.geogebra.common.util.lang.Unicode;
import org.geogebra.keyboard.base.Action;
import org.geogebra.web.html5.gui.util.KeyboardLocale;
import org.geogebra.web.html5.gui.util.NoDragImage;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.SimplePanel;
/**
* on screen keyboard containing mathematical symbols and formulas
*/
public abstract class KBBase extends PopupPanel {
private KeyboardLocalization localization;
final String[] keysGreek = {
"\u03D5\u03A6\u03C2\u03A3\u03B5\u0395\u03C1\u03A1\u03C4\u03A4\u03C5\u03A5\u03B8\u0398\u03B9\u0399\u03BF\u039F\u03C0\u03A0",
"\u03B1\u0391\u03C3\u03A3\u03B4\u0394\u03C6\u03A6\u03B3\u0393\u03B7\u0397\u03BE\u039E\u03BA\u039A\u03BB\u039B",
// double-space at start for where <Shift> is
" \u03B6\u0396\u03C7\u03A7\u03C8\u03A8\u03C9\u03A9\u03B2\u0392\u03BD\u039D\u03BC\u039C''" };
/**
* all supported locales and the associated keyboardLocal, e.g. en_UK - en,
* ca - es, de_AT - de
*/
private final static HashMap<String, String> supportedLocales = new HashMap<String, String>();
static {
// supportedLocales.put(Language.Arabic.localeGWT, "ar");
// supportedLocales.put(Language.Arabic_Morocco.localeGWT, "ar");
// supportedLocales.put(Language.Arabic_Tunisia.localeGWT, "ar");
// supportedLocales.put(Language.Armenian.localeGWT, "hy"); some letters
// missing
supportedLocales.put(Language.Basque.localeGWT, "es");
supportedLocales.put(Language.Bosnian.localeGWT, "sl");
// supportedLocales.put(Language.Bulgarian.localeGWT, "bg");
supportedLocales.put(Language.Catalan.localeGWT, "ca");
// supportedLocales.put(Language.Chinese_Simplified, value);
// supportedLocales.put(Language.Chinese_Traditional, value);
supportedLocales.put(Language.Croatian.localeGWT, "sl");
supportedLocales.put(Language.Czech.localeGWT, "cs");
supportedLocales.put(Language.Danish.localeGWT, "da");
supportedLocales.put(Language.Dutch.localeGWT, "en");
// supportedLocales.put(Language.Dutch_Belgium.localeGWT, value);
supportedLocales.put(Language.English_Australia.localeGWT, "en");
supportedLocales.put(Language.English_UK.localeGWT, "en");
supportedLocales.put(Language.English_US.localeGWT, "en");
supportedLocales.put(Language.Estonian.localeGWT, "et");
supportedLocales.put(Language.Filipino.localeGWT, "en");
supportedLocales.put(Language.Finnish.localeGWT, "fi");
supportedLocales.put(Language.French.localeGWT, "fr");
supportedLocales.put(Language.Galician.localeGWT, "es");
// supportedLocales.put(Language.Georgian.localeGWT, "ka");
supportedLocales.put(Language.German.localeGWT, "de");
supportedLocales.put(Language.German_Austria.localeGWT, "de");
supportedLocales.put(Language.Greek.localeGWT, "en");
// supportedLocales.put(Language.Hebrew.localeGWT, "iw");
// supportedLocales.put(Language.Hindi.localeGWT, "hi");
supportedLocales.put(Language.Hungarian.localeGWT, "hu");
supportedLocales.put(Language.Icelandic.localeGWT, "is");
supportedLocales.put(Language.Indonesian.localeGWT, "en");
supportedLocales.put(Language.Italian.localeGWT, "it");
// supportedLocales.put(Language.Japanese.localeGWT, value);
// supportedLocales.put(Language.Kazakh.localeGWT, "kk");
// supportedLocales.put(Language.Korean.localeGWT, "ko");
supportedLocales.put(Language.Latvian.localeGWT, "en");
supportedLocales.put(Language.Lithuanian.localeGWT, "lt");
// supportedLocales.put(Language.Macedonian.localeGWT, "mk");
supportedLocales.put(Language.Malay.localeGWT, "ms");
// supportedLocales.put(Language.Mongolian.localeGWT, "mn");
// supportedLocales.put(Language.Nepalese.localeGWT, "ne");
supportedLocales.put(Language.Norwegian_Bokmal.localeGWT, "no");
supportedLocales.put(Language.Norwegian_Nynorsk.localeGWT, "no");
// supportedLocales.put(Language.Persian.localeGWT, "fa");
supportedLocales.put(Language.Polish.localeGWT, "en");
supportedLocales.put(Language.Portuguese_Brazil.localeGWT, "pt");
supportedLocales.put(Language.Portuguese_Portugal.localeGWT, "pt");
supportedLocales.put(Language.Romanian.localeGWT, "ro");
// supportedLocales.put(Language.Russian.localeGWT, "ru");
supportedLocales.put(Language.Serbian.localeGWT, "sl");
// supportedLocales.put(Language.Sinhala.localeGWT, "si");
supportedLocales.put(Language.Slovak.localeGWT, "sk");
supportedLocales.put(Language.Slovenian.localeGWT, "sl");
supportedLocales.put(Language.Spanish.localeGWT, "es");
supportedLocales.put(Language.Spanish_ES.localeGWT, "es");
supportedLocales.put(Language.Spanish_UY.localeGWT, "es");
supportedLocales.put(Language.Swedish.localeGWT, "sv");
// supportedLocales.put(Language.Tamil.localeGWT, "ta");
// supportedLocales.put(Language.Thai.localeGWT, "th");
supportedLocales.put(Language.Turkish.localeGWT, "tr");
// supportedLocales.put(Language.Ukrainian.localeGWT, "uk");
// supportedLocales.put(Language.Uyghur.localeGWT, "ug");
supportedLocales.put(Language.Valencian.localeGWT, "es");
// supportedLocales.put(Language.Vietnamese.localeGWT, value);
supportedLocales.put(Language.Welsh.localeGWT, "en");
// supportedLocales.put(Language.Yiddish.localeGWT, "ji");
}
/**
* minimum width of the whole application to use normal font (small font
* otherwise)
*/
protected static final int MIN_WIDTH_FONT = 485;
/**
* number of buttons in each horizontal line of buttons on the keyboard
*/
protected static final int KEY_PER_ROW = 12;
/**
* number of buttons with one letter on them
*/
protected static final int NUM_LETTER_BUTTONS = 38;
// text of the buttons:
/**
* Greek letters
*/
protected static final String GREEK = Unicode.alphaBetaGamma;
/**
* letters of the given language
*/
protected static final String TEXT = KeyboardMode.TEXT.getInternalName();
/**
* numbers and basic math
*/
protected static final String NUMBER = KeyboardMode.NUMBER
.getInternalName();
/**
* advanced math
*/
protected static final String SPECIAL_CHARS = KeyboardMode.SPECIAL_CHARS
.getInternalName();
/**
*
*/
protected static final String PAGE_ONE_OF_TWO = "1/2";
protected static final String PAGE_TWO_OF_TWO = "2/2";
public static final int SMALL_HEIGHT = 131;
public static final int BIG_HEIGHT = 186;
// images of the buttons:
private final ImageResource SHIFT = KeyboardResources.INSTANCE
.keyboard_shiftOld();
private final ImageResource SHIFT_DOWN = KeyboardResources.INSTANCE
.keyboard_shiftDownOld();
private final ImageResource ENTER = KeyboardResources.INSTANCE
.keyboard_enter();
private final ImageResource BACKSPACE = KeyboardResources.INSTANCE
.keyboard_backspaceOld();
private final ImageResource ARROW_LEFT = KeyboardResources.INSTANCE
.keyboard_arrowLeft();
private final ImageResource ARROW_RIGHT = KeyboardResources.INSTANCE
.keyboard_arrowRight();
protected HorizontalPanel contentNumber = new HorizontalPanel();
protected HorizontalPanel contentSpecialChars = new HorizontalPanel();
protected FlowPanel contentLetters = new FlowPanel();
public KeyboardListener processField;
protected KeyboardMode mode = KeyboardMode.NUMBER;
protected KeyPanelBase letters;
protected KeyBoardButtonBase switchABCGreek;
private int numVisibleButtons;
/**
* application that is used
*/
protected final HasKeyboard app;
protected boolean accentDown = false;
private KeyBoardButtonBase accentButton;
/** contains the unicode string for the specific letter with acute accent */
private HashMap<String, String> accentAcute = new HashMap<String, String>();
/** contains the unicode string for the specific letter with grave accent */
private HashMap<String, String> accentGrave = new HashMap<String, String>();
/** contains the unicode string for the specific letter with caron accent */
private HashMap<String, String> accentCaron = new HashMap<String, String>();
/** contains the unicode string for the specific letter with circumflex */
private HashMap<String, String> accentCircumflex = new HashMap<String, String>();
/**
* positioning (via setPopupPosition) needs to be enabled in order to
* prevent automatic positioning in the constructor
*/
public boolean enablePositioning = false;
/**
* listener for updates of the keyboard structure
*/
public UpdateKeyBoardListener updateKeyBoardListener;
protected boolean shiftIsDown = false;
protected boolean greekActive = false;
protected boolean keyboardWanted = false;
/** language of application */
protected String keyboardLocale = "";
private KeyBoardButtonFunctionalBase shiftButton;
private KeyBoardButtonBase backspaceButton;
protected KeyboardLocale loc;
/**
* buttons that need to be updated when the language is changed and their
* default label (which can be found in loc.getPlain)
*/
protected HashMap<KeyBoardButtonBase, String> updateButton = new HashMap<KeyBoardButtonBase, String>();
protected KeyPanelBase firstPageChars;
protected KeyPanelBase secondPageChars;
protected SimplePanel specialCharContainer;
private boolean isSmallKeyboard = false;
private ButtonHandler handler;
protected void initAccentAcuteLetters() {
accentAcute.put("a", "\u00e1");
accentAcute.put("A", "\u00c1");
accentAcute.put("e", "\u00e9");
accentAcute.put("E", "\u00C9");
accentAcute.put("i", "\u00ed");
accentAcute.put("I", "\u00cd");
accentAcute.put("l", "\u013A");
accentAcute.put("L", "\u0139");
accentAcute.put("o", "\u00f3");
accentAcute.put("O", "\u00d3");
accentAcute.put("r", "\u0155");
accentAcute.put("R", "\u0154");
accentAcute.put("u", "\u00fa");
accentAcute.put("U", "\u00da");
accentAcute.put("y", "\u00fd");
accentAcute.put("Y", "\u00dd");
}
protected void initAccentGraveLetters() {
accentGrave.put("a", "\u00e0");
accentGrave.put("A", "\u00c0");
accentGrave.put("e", "\u00e8");
accentGrave.put("E", "\u00C8");
accentGrave.put("i", "\u00ec");
accentGrave.put("I", "\u00cc");
accentGrave.put("o", "\u00f2");
accentGrave.put("O", "\u00d2");
accentGrave.put("u", "\u00f9");
accentGrave.put("U", "\u00d9");
}
protected void initAccentCaronLetters() {
accentCaron.put("c", "\u010d");
accentCaron.put("C", "\u010c");
accentCaron.put("d", "\u010F");
accentCaron.put("D", "\u010e");
accentCaron.put("e", "\u011b");
accentCaron.put("E", "\u011A");
accentCaron.put("l", "\u013E");
accentCaron.put("L", "\u013D");
accentCaron.put("n", "\u0148");
accentCaron.put("N", "\u0147");
accentCaron.put("r", "\u0159");
accentCaron.put("R", "\u0158");
accentCaron.put("s", "\u0161");
accentCaron.put("S", "\u0160");
accentCaron.put("t", "\u0165");
accentCaron.put("T", "\u0164");
accentCaron.put("z", "\u017e");
accentCaron.put("Z", "\u017d");
}
protected void initAccentCircumflexLetters() {
accentCircumflex.put("a", "\u00e2");
accentCircumflex.put("A", "\u00c2");
accentCircumflex.put("e", "\u00ea");
accentCircumflex.put("E", "\u00Ca");
accentCircumflex.put("i", "\u00ee");
accentCircumflex.put("I", "\u00ce");
accentCircumflex.put("o", "\u00f4");
accentCircumflex.put("O", "\u00d4");
accentCircumflex.put("u", "\u00fb");
accentCircumflex.put("U", "\u00db");
}
/**
* should not be called; use getInstance instead
*
* @param autoHide
* whether or not the popup should be automatically hidden when
* the user clicks outside of it or the history token changes.
*/
public KBBase(boolean autoHide, HasKeyboard app) {
super(autoHide);
this.app = app;
localization = new KeyboardLocalization();
}
/**
* @param handler
* click handler for buttons
*/
public void setButtonHandler(ButtonHandler handler) {
this.handler = handler;
}
@Override
public void setPopupPosition(int left, int top) {
if (enablePositioning) {
super.setPopupPosition(left, top);
}
}
protected void createKeyBoard() {
this.updateButton = new HashMap<KeyBoardButtonBase, String>();
// number - keyboard
createFunctionsKeyPanel();
createNumbersKeyPanel();
createControlKeyPanel();
// letter - keyboard
createLettersKeyPanel();
// special characters - keyboard
createSpecialCharKeyPanel();
FlowPanel p = new FlowPanel();
contentNumber.addStyleName("KeyBoardContentNumbers");
p.add(contentNumber);
p.add(contentLetters);
p.add(contentSpecialChars);
p.add(getCloseButton());
add(p);
resetKeyboardState();
}
/**
* sets width of the onScreenKeyboard and adds specific styleNames if
* keyboard needs to be scaled
*/
public void updateSize() {
if (app.getInnerWidth() < 10) {
return;
}
// -10 because of padding
this.setWidth(app.getInnerWidth() - 10 + "px");
boolean shouldBeSmall = app.needsSmallKeyboard();
if (shouldBeSmall && !isSmallKeyboard) {
this.addStyleName("lowerHeight");
this.isSmallKeyboard = true;
} else if (!shouldBeSmall && isSmallKeyboard) {
this.removeStyleName("lowerHeight");
this.isSmallKeyboard = false;
}
updateHeight();
}
private void updateHeight() {
if (app != null) {
app.updateKeyboardHeight();
}
}
/**
* adds a specific styleName to the keyboard (if keyboard has to be scaled
* or not)
*/
public void setStyleName() {
if (app.getInnerWidth() < getMinWidthWithoutScaling()) {
addStyleName("scale");
removeStyleName("normal");
removeStyleName("smallerFont");
if (app.getInnerWidth() < MIN_WIDTH_FONT) {
addStyleName("smallerFont");
}
} else {
addStyleName("normal");
removeStyleName("scale");
removeStyleName("smallerFont");
}
}
/**
* check the minimum width. Either width of ABC panel or 123 panel. 70 =
* width of button; 82 = padding
*
* @return
*/
private int getMinWidthWithoutScaling() {
int abc = numVisibleButtons * 70 + 82;
int numbers = 850;
return Math.max(abc, numbers);
}
protected SimplePanel getCloseButton() {
NoDragImage image = new NoDragImage(KeyboardResources.INSTANCE
.keyboard_close().getSafeUri().asString());
image.addStyleName("closeIcon");
SimplePanel closePanel = new SimplePanel(image);
closePanel.addStyleName("keyBoardClosePanel");
closePanel.addDomHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if ((KBBase.this.processField != null)
&& KBBase.this.processField.isSVCell()) {
// hideInSV = true;
processField.onKeyboardClosed();
}
keyboardWanted = false;
updateKeyBoardListener.keyBoardNeeded(false, null);
}
}, ClickEvent.getType());
return closePanel;
}
protected void createFunctionsKeyPanel() {
KeyPanelBase functions = new KeyPanelBase();
functions.addStyleName("KeyPanelFunction");
// fill first row
int index = 0;
addButton("x", index, functions);
addButton("y", index, functions);
addButton("a^2", Unicode.Superscript_2 + "", index, functions)
.addStyleName("supScript");
addButton(Unicode.SQUARE_ROOT + "", index, functions);
// fill next row
index++;
addButton("a^x", KeyboardConstants.A_POWER_X, index, functions)
.addStyleName("supScript");
addButton("|x|", "abs", index, functions);
// just for testing:
// addButton(Unicode.LFLOOR + "x" + Unicode.RFLOOR, "floor", index,
// functions);
// addButton(Unicode.LCEIL + "x" + Unicode.RCEIL, "ceil", index,
// functions);
addButton(Unicode.DEGREE, index, functions);
addButton(Unicode.PI_STRING, index, functions);
// fill next row
index++;
addButton("(", index, functions);
addButton(")", index, functions);
addButton("<", index, functions);
addButton(">", index, functions);
// fill next row
index++;
addFunctionalButton(index, functions, TEXT, Action.SWITCH_KEYBOARD)
.addStyleName("switchKeyboard");
addFunctionalButton(index, functions, SPECIAL_CHARS,
Action.SWITCH_KEYBOARD).addStyleName("switchKeyboard");
addFunctionalButton(index, functions, GREEK, Action.SWITCH_KEYBOARD)
.addStyleName("switchKeyboard");
addButton(",", index, functions);
contentNumber.add(functions);
}
protected void createNumbersKeyPanel() {
KeyPanelBase numbers = new KeyPanelBase();
numbers.addStyleName("KeyPanelNum");
// fill first row
int index = 0;
addButton("7", index, numbers);
addButton("8", index, numbers);
addButton("9", index, numbers);
addButton(Unicode.DIVIDE, Unicode.DIVIDE, index, numbers);
// fill next row
index++;
addButton("4", index, numbers);
addButton("5", index, numbers);
addButton("6", index, numbers);
addButton(Unicode.MULTIPLY + "", "*", index, numbers);
// fill next row
index++;
addButton("1", index, numbers);
addButton("2", index, numbers);
addButton("3", index, numbers);
addButton(Unicode.MINUS + "", index, numbers);
// fill next row
index++;
addButton("0", index, numbers);
addButton(".", index, numbers);
addButton("=", index, numbers);
addButton("+", index, numbers);
contentNumber.add(numbers);
}
protected void createControlKeyPanel() {
contentNumber.add(getControlKeyPanel());
}
/**
* @return
*/
protected KeyPanelBase getControlKeyPanel() {
KeyPanelBase control = new KeyPanelBase();
control.addStyleName("KeyPanelControl");
int index = 0;
addFunctionalButton(BACKSPACE, Action.BACKSPACE_DELETE, index, control)
.addStyleName("backspace");
index++;
addFunctionalButton(ENTER, Action.RETURN_ENTER, index, control)
.addStyleName(
"enter");
index++;
addFunctionalButton(ARROW_LEFT, Action.LEFT_CURSOR, index, control)
.addStyleName("arrow");
addFunctionalButton(ARROW_RIGHT, Action.RIGHT_CURSOR, index, control)
.addStyleName("arrow");
return control;
}
protected void createLettersKeyPanel() {
contentLetters.addStyleName("contentLetters");
letters = new KeyPanelBase();
letters.addStyleName("KeyPanelLetters");
// create first row
int index = 0;
for (int i = 0; i < KEY_PER_ROW; i++) {
addButton("", index, letters);
}
// create second row
index++;
for (int i = 0; i < KEY_PER_ROW; i++) {
addButton("", index, letters);
}
// create third row
index++;
shiftButton = addFunctionalButton(SHIFT, Action.CAPS_LOCK, index,
letters);
shiftButton.addStyleName("shift");
for (int i = 0; i < KEY_PER_ROW - 1; i++) {
addButton("", index, letters);
}
backspaceButton = addFunctionalButton(BACKSPACE,
Action.BACKSPACE_DELETE,
index, letters);
backspaceButton.addStyleName("delete");
// fill forth row - fixed buttons for all languages
index++;
addFunctionalButton(index, letters, NUMBER, Action.SWITCH_KEYBOARD)
.addStyleName("switchKeyboard");
addFunctionalButton(index, letters, SPECIAL_CHARS,
Action.SWITCH_KEYBOARD).addStyleName("switchKeyboard");
switchABCGreek = addFunctionalButton(index, letters, GREEK,
Action.SWITCH_KEYBOARD);
switchABCGreek.addStyleName("switchKeyboard");
addButton(" ", index, letters).addStyleName("space");
addFunctionalButton(ARROW_LEFT, Action.LEFT_CURSOR, index, letters);
addFunctionalButton(ARROW_RIGHT, Action.RIGHT_CURSOR, index, letters);
addFunctionalButton(ENTER, Action.RETURN_ENTER, index, letters);
contentLetters.add(letters);
}
protected void createSpecialCharKeyPanel() {
contentSpecialChars.addStyleName("KeyBoardContentSpecialChars");
KeyPanelBase functions = new KeyPanelBase();
functions.addStyleName("KeyPanelFunction");
// fill first row
int index = 0;
updateButton.put(
addButton(loc.getFunction("sin"), index, functions),
"sin");
updateButton.put(
addButton(loc.getFunction("cos"), index, functions),
"cos");
updateButton.put(
addButton(loc.getFunction("tan"), index, functions),
"tan");
addButton("e^x", Unicode.EULER_STRING + "^", index, functions)
.addStyleName("supScript");
// fill second row
index++;
KeyBoardButtonBase button;
button = addButton(loc.getFunction("sin") + "^-1",
loc.getFunction("asin"), index,
functions);
button.addStyleName("supScript");
updateButton.put(button, "sin" + "^-1");
button = addButton(loc.getFunction("cos") + "^-1",
loc.getFunction("acos"),
index, functions);
button.addStyleName("supScript");
updateButton.put(button, "cos" + "^-1");
button = addButton(loc.getFunction("tan") + "^-1",
loc.getFunction("atan"),
index, functions);
button.addStyleName("supScript");
updateButton.put(button, "tan" + "^-1");
addButton("ln", index, functions);
// fill third row
index++;
updateButton.put(
addButton(loc.getFunction("sinh"), "sinh", index,
functions),
"sinh");
updateButton.put(
addButton(loc.getFunction("cosh"), "cosh", index,
functions),
"cosh");
updateButton.put(
addButton(loc.getFunction("tanh"), "tanh", index,
functions),
"tanh");
addButton("log_10", "log", index, functions);
// fill forth row
index++;
addFunctionalButton(index, functions, TEXT, Action.SWITCH_KEYBOARD)
.addStyleName("switchKeyboard");
addFunctionalButton(index, functions, NUMBER, Action.SWITCH_KEYBOARD)
.addStyleName("switchKeyboard");
addFunctionalButton(index, functions, GREEK, Action.SWITCH_KEYBOARD)
.addStyleName("switchKeyboard");
addButton("nroot", index, functions);
firstPageChars = new KeyPanelBase();
firstPageChars.addStyleName("KeyPanelNum");
// fill first row
index = 0;
addButton("[", index, firstPageChars);
addButton("]", index, firstPageChars);
addButton("!", index, firstPageChars);
addButton(Unicode.IMAGINARY, index, firstPageChars);
// fill second row
index++;
addButton("{", index, firstPageChars);
addButton("}", index, firstPageChars);
addButton("a_n", "_", index, firstPageChars);
addButton(Unicode.OPEN_DOUBLE_QUOTE + " " + Unicode.CLOSE_DOUBLE_QUOTE,
"quotes", index, firstPageChars);
// fill third row
index++;
addButton(Unicode.LESS_EQUAL + "", index, firstPageChars);
addButton(Unicode.GREATER_EQUAL + "", index, firstPageChars);
addButton("%", index, firstPageChars);
addButton("$", index, firstPageChars);
// fill forth row
index++;
addFunctionalButton(index, firstPageChars, PAGE_ONE_OF_TWO,
Action.SWITCH_KEYBOARD).addStyleName("switchKeyboard");
addButton(Unicode.COLON_EQUALS, index, firstPageChars);
addButton(":", index, firstPageChars);
addButton(";", index, firstPageChars);
/** create second page of special chars */
secondPageChars = new KeyPanelBase();
secondPageChars.addStyleName("KeyPanelNum");
// fill first row
index = 0;
addButton(Unicode.INFINITY + "", index, secondPageChars);
addButton(Unicode.QUESTEQ, index, secondPageChars);
addButton(Unicode.NOTEQUAL, index, secondPageChars);
addButton("#", index, secondPageChars);
// fill second row
index++;
addButton(Unicode.AND, index, secondPageChars);
addButton(Unicode.OR, index, secondPageChars);
addButton(Unicode.IMPLIES, index, secondPageChars);
addButton(Unicode.NOT, index, secondPageChars);
// fill third row
index++;
addButton(Unicode.IS_ELEMENT_OF + "", index, secondPageChars);
addButton(Unicode.IS_SUBSET_OF_STRICT + "", index, secondPageChars);
addButton(Unicode.IS_SUBSET_OF + "", index, secondPageChars);
addButton(Unicode.ANGLE, index, secondPageChars);
// fill forth row
index++;
addFunctionalButton(index, secondPageChars, PAGE_TWO_OF_TWO,
Action.SWITCH_KEYBOARD).addStyleName("switchKeyboard");
addButton(Unicode.PARALLEL, index, secondPageChars);
addButton(Unicode.PERPENDICULAR + "", index, secondPageChars);
addButton(Unicode.VECTOR_PRODUCT + "", index, secondPageChars);
specialCharContainer = new SimplePanel();
specialCharContainer.add(firstPageChars);
contentSpecialChars.add(functions);
contentSpecialChars.add(specialCharContainer);
contentSpecialChars.add(getControlKeyPanel());
}
/**
* adds a button to the row with index {@code row} within the given
* keyPanel. Use this only for {@link KeyBoardButtonBase} with same caption
* and feedback.
*
* @param caption
* of button
* @param index
* of row
* @param panel
* {@link KeyPanelBase}
* @return {@link KeyBoardButtonBase}
*/
protected KeyBoardButtonBase addButton(String caption, int index,
KeyPanelBase panel) {
return addButton(caption, caption, index, panel);
}
/**
* adds a button to the row with index {@code row} within the given
* keyPanel.
*
* @param caption
* of button
* @param feedback
* of button
* @param index
* of row
* @param panel
* {@link KeyPanelBase}
* @return {@link KeyBoardButtonBase}
*/
protected KeyBoardButtonBase addButton(String caption, String feedback,
int index, KeyPanelBase panel) {
KeyBoardButtonBase button = new KeyBoardButtonBase(caption, feedback,
handler);
panel.addToRow(index, button);
return button;
}
/**
* adds a functional button to the row with index {@code row} within the
* given keyPanel. Use this only for {@link KeyBoardButtonFunctionalBase}
* with an String as caption.
*
* @param index
* of row
* @param keyPanel
* {@link KeyPanelBase}
* @param caption
* of button
* @param action
* {@link Action}
* @return {@link KeyBoardButtonFunctionalBase}
*/
protected KeyBoardButtonFunctionalBase addFunctionalButton(int index,
KeyPanelBase keyPanel, String caption, Action action) {
KeyBoardButtonFunctionalBase button = new KeyBoardButtonFunctionalBase(
caption, handler, action);
keyPanel.addToRow(index, button);
return button;
}
/**
* adds a functional button to the row with index {@code row} within the
* given keyPanel. Use this only for {@link KeyBoardButtonFunctionalBase}
* with an image.
*
* @param image
* of the button
* @param index
* of row
* @param keyPanel
* {@link KeyPanelBase}
* @param action
* {@link Action}
* @return {@link KeyBoardButtonFunctionalBase}
*/
protected KeyBoardButtonFunctionalBase addFunctionalButton(
ImageResource image, Action action, int index, KeyPanelBase keyPanel) {
KeyBoardButtonFunctionalBase button = new KeyBoardButtonFunctionalBase(
image, handler, action);
keyPanel.addToRow(index, button);
return button;
}
/**
* set the text field that will receive the input from the keyboard
*
* @param processing
* the text field to be used
*/
public void setProcessing(KeyboardListener processing) {
// checking if app is null and instance of AppW needed only avoid
// exceptions at checking feature. So at deleting feature check, you can
// delete "app != null && app instanceof AppW" too
if (processField != null && processField.getField() != null) {
if (processing == null
|| processField.getField() != processing.getField()) {
endEditing();
}
}
this.processField = processing;
}
public void endEditing() {
if (processField != null) {
processField.endEditing();
}
}
protected void processAccent(String accent, KeyBoardButtonBase source) {
if (accentDown && source != accentButton) {
removeAccents();
setToAccents(accent, source);
} else if (!accentDown) {
setToAccents(accent, source);
} else {
removeAccents();
}
}
protected void removeAccents() {
for (KeyBoardButtonBase button : letters.getButtons()) {
if (hasAccent(button.getCaption())) {
button.setCaption(getWithoutAccent(button.getCaption()));
}
}
if (accentButton != null) {
accentButton.removeStyleName("accentDown");
}
accentDown = false;
}
/**
* @param accent
*/
protected void setToAccents(String accent, KeyBoardButtonBase source) {
accentButton = source;
accentButton.addStyleName("accentDown");
HashMap<String, String> accents = getAccentList(accent);
for (KeyBoardButtonBase button : letters.getButtons()) {
if (canHaveAccent(button.getCaption(), accents)) {
button.setCaption(accents.get(button.getCaption()));
}
}
accentDown = true;
}
/**
* @param accent
* @return
*/
protected HashMap<String, String> getAccentList(String accent) {
HashMap<String, String> accents;
if (accent.equals(Unicode.ACCENT_ACUTE)) {
accents = accentAcute;
} else if (accent.equals(Unicode.ACCENT_CARON)) {
accents = accentCaron;
} else if (accent.equals(Unicode.ACCENT_CIRCUMFLEX)) {
accents = accentCircumflex;
} else {
accents = accentGrave;
}
return accents;
}
private static boolean canHaveAccent(String letter,
HashMap<String, String> accents) {
return accents.get(letter) != null;
}
protected boolean hasAccent(String letter) {
return accentAcute.containsValue(letter)
|| accentCaron.containsValue(letter)
|| accentCircumflex.containsValue(letter)
|| accentGrave.containsValue(letter);
}
/**
* check {@link #hasAccent(String)} before calling this
*
* @param letter
* String
* @return letter without accent
*/
protected String getWithoutAccent(String letter) {
HashMap<String, String> accents;
if (accentAcute.containsValue(letter)) {
accents = accentAcute;
} else if (accentCaron.containsValue(letter)) {
accents = accentCaron;
} else if (accentCircumflex.containsValue(letter)) {
accents = accentCircumflex;
} else {
accents = accentGrave;
}
for (Entry<String, String> entry : accents.entrySet()) {
if (accents.get(entry.getKey()).equals(letter)) {
return entry.getKey();
}
}
return letter;
}
/**
* @param text
* @return {@code true} if the given text is an accent
*/
protected static boolean isAccent(String text) {
return text.equals(Unicode.ACCENT_ACUTE)
|| text.equals(Unicode.ACCENT_CARON)
|| text.equals(Unicode.ACCENT_GRAVE)
|| text.equals(Unicode.ACCENT_CIRCUMFLEX);
}
protected void processShift() {
shiftIsDown = !shiftIsDown;
String local = greekActive ? Language.Greek.localeGWT : keyboardLocale;
if (shiftIsDown) {
shiftButton.setPicture(SHIFT_DOWN);
updateKeys("shiftDown", local);
} else {
shiftButton.setPicture(SHIFT);
updateKeys("lowerCase", local);
}
}
public void setListener(UpdateKeyBoardListener listener) {
this.updateKeyBoardListener = listener;
}
/**
* @param mode
* the keyboard mode
*/
public abstract void setKeyboardMode(final KeyboardMode mode);
protected void setToGreekLetters() {
setKeyboardMode(KeyboardMode.TEXT);
greekActive = true;
switchABCGreek.setCaption(KeyboardMode.TEXT.getInternalName());
updateKeys("lowerCase", Language.Greek.localeGWT);
setStyleName();
if (shiftIsDown) {
processShift();
}
}
/**
* @return the keyboard mode
*/
public KeyboardMode getKeyboardMode() {
return mode;
}
/**
* @return true iff the systems virtual keyboard should be used
*/
public boolean useSystemVirtualKeyboard() {
return mode == KeyboardMode.TEXT;
}
/**
* set the keyboard state to the default state
*/
public void resetKeyboardState() {
mode = KeyboardMode.NUMBER;
contentNumber.setVisible(true);
contentLetters.setVisible(false);
contentSpecialChars.setVisible(false);
if (shiftIsDown) {
processShift();
}
}
/**
* updates the keys to the given language
*
* @param updateSection
* "lowerCase" or "shiftDown"
* @param language
* String
*/
/**
* updates the keys to the given language
*
* @param updateSection
* "lowerCase" or "shiftDown"
* @param language
* String
*/
protected final void updateKeys(String updateSection, String language) {
// update letter keys
ArrayList<KeyBoardButtonBase> buttons = this.letters.getButtons();
int key = 0;
boolean upperCase = "shiftDown".equals(updateSection);
int offset = upperCase ? 1 : 0;
String[] keys;
if (supportedLocales.containsKey("ko")
|| !loc.getLocaleStr().startsWith("ko")) {
keys = new String[] { loc.getKeyboardRow(1), loc.getKeyboardRow(2),
// first key is shift, so need " " otherwise 'z' is hidden
" " + loc.getKeyboardRow(3) };
} else {
keys = new String[] { "qQwWeErRtTyYuUiIoOpP",
"aAsSdDfFgGhHjJkKlL''", "zZxXcCvVbBnNmM" };
}
if (Language.Greek.localeGWT.equals(language)) {
keys = keysGreek;
}
for (int row = 0; row <= 2; row++) {
for (int i = 0; i < KEY_PER_ROW; i++) {
KeyBoardButtonBase button = buttons.get(key++);
if (!(button instanceof KeyBoardButtonFunctionalBase)) {
String newCaption = "";
int index = 2 * i + offset;
// not all 12 keys are used in all languages
if (index < keys[row].length()) {
newCaption = "" + keys[row].charAt(2 * i + offset);
}
if ("".equals(newCaption)) {
button.setVisible(false);
button.getElement().getParentElement()
.addClassName("hidden");
} else {
button.setVisible(true);
button.getElement().getParentElement()
.removeClassName("hidden");
button.setCaption(newCaption);
}
}
}
}
// update e.g. button with sin/cos/tan according to the new language
for (Entry<KeyBoardButtonBase, String> entry : updateButton
.entrySet()) {
KeyBoardButtonBase b = entry.getKey();
String captionPlain = entry.getValue();
if (captionPlain.endsWith("^-1")) {
// e.g. for "sin^-1" only "sin" is translated
captionPlain = captionPlain.substring(0,
captionPlain.lastIndexOf("^-1"));
// localize feedback and caption independently
b.setCaption(loc.getFunction(captionPlain) + "^-1",
loc.getFunction("a" + captionPlain));
} else {
// same feedback and caption
b.setCaption(loc.getFunction(captionPlain));
}
}
if (processField != null) {
processField.updateForNewLanguage(loc);
}
checkStyle();
}
/**
* get translations for the onScreenKeyboard-buttons
*
* @param key
* String to translate
* @param updateSection
* "lowerCase" or "shiftDown"
* @param language
* language of the key
* @return String for keyboardButton
*/
protected String getNewCaption(String key, String updateSection,
String language) {
return localization.getKey(key, updateSection, language);
}
/**
* we need smaller buttons (shift and backspace) if the third row has only
* one button less than the upper two rows. otherwise the buttons would
* overlap.
*/
protected void checkStyle() {
int first = countVisibleButtons(this.letters.getRows().get(0));
int second = countVisibleButtons(this.letters.getRows().get(1));
int third = countVisibleButtons(this.letters.getRows().get(2));
this.numVisibleButtons = Math.max(first, second);
if (numVisibleButtons - third < 1) {
shiftButton.addStyleName("small");
backspaceButton.addStyleName("small");
} else {
shiftButton.removeStyleName("small");
backspaceButton.removeStyleName("small");
}
}
/**
* @param row
* {@link HorizontalPanel}
* @return the number of visible buttons of the given row
*/
private static int countVisibleButtons(HorizontalPanel row) {
int numOfButtons = 0;
for (int i = 0; i < row.getWidgetCount(); i++) {
if (row.getWidget(i).isVisible()) {
numOfButtons++;
}
}
return numOfButtons;
}
/**
* @param i
* index of button
* @return key for the translation-files (keyboard.js).
*/
protected static String generateKey(int i) {
if (i < KEY_PER_ROW) {
return "B0_" + i;
} else if (i < KEY_PER_ROW + KEY_PER_ROW) {
return "B1_" + (i - KEY_PER_ROW);
} else {
return "B2_" + (i - 1 - KEY_PER_ROW - KEY_PER_ROW);
}
}
/**
* loads the translation-files for the active language if it is different
* from the last loaded language and sets the {@link #keyboardLocale} to the
* new language
*/
public void checkLanguage() {
String locale = app.getLocalization().getLocaleStr();
String newKeyboardLocale = supportedLocales.get(locale);
if (newKeyboardLocale != null
&& keyboardLocale.equals(newKeyboardLocale)) {
return;
}
if (newKeyboardLocale != null) {
this.keyboardLocale = newKeyboardLocale;
} else {
this.keyboardLocale = Language.English_US.localeGWT;
}
updateKeys("lowerCase", keyboardLocale);
setStyleName();
}
/**
* @return {@code true} if the keyboard should be shown
*/
public boolean shouldBeShown() {
return this.keyboardWanted;
}
public void showOnFocus() {
this.keyboardWanted = true;
}
/**
* @param gwtLang
* language
* @param language
* language description
*/
public final static void addSupportedLocale(Language gwtLang,
String language) {
supportedLocales.put(gwtLang.localeGWT, language);
}
}