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); } }