package org.geogebra.web.web.gui.view.algebra; import org.geogebra.common.gui.inputfield.InputHelper; import org.geogebra.common.io.latex.GeoGebraSerializer; import org.geogebra.common.kernel.commands.EvalInfo; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.main.Feature; import org.geogebra.common.main.error.ErrorHandler; import org.geogebra.common.util.AsyncOperation; import org.geogebra.common.util.StringUtil; import org.geogebra.common.util.debug.Log; import org.geogebra.web.html5.gui.util.CancelEventTimer; import org.geogebra.web.web.gui.GuiManagerW; import org.geogebra.web.web.gui.inputfield.InputSuggestions; import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.user.client.Timer; import com.himamis.retex.editor.share.event.MathFieldListener; import com.himamis.retex.editor.share.model.MathSequence; import com.himamis.retex.editor.web.MathFieldW; /** * @author Laszlo * */ public class LatexTreeItemController extends RadioTreeItemController implements MathFieldListener, BlurHandler { private InputSuggestions sug; private RetexKeyboardListener retexListener; /** whether blur listener is disabled */ boolean preventBlur = false; /** * @param item * AV item */ public LatexTreeItemController(LatexTreeItem item) { super(item); } @Override protected void startEdit(boolean substituteNumbers) { if (item.isInputTreeItem() && item.onEditStart(false)) { setOnScreenKeyboardTextField(); } else { super.startEdit(substituteNumbers); } } @Override public void onBlur(BlurEvent event) { if (preventBlur) { return; } item.onEnter(false); if (item.isEmpty() && item.isInputTreeItem()) { item.addDummyLabel(); item.setItemWidth(item.getAV().getMaxItemWidth()); } if (item.getAV().isNodeTableEmpty()) { // #5245#comment:8, cases B and C excluded item.updateGUIfocus(event == null ? this : event.getSource(), true); } } /** * @param keepFocus * whether focus should stay */ public void onEnter(final boolean keepFocus) { if (app.has(Feature.AV_SINGLE_TAP_EDIT) && item.isInputTreeItem() && item.isEmpty()) { item.styleEditor(); item.addDummyLabel(); return; } item.setShowInputHelpPanel(false); if (item.geo == null) { if (StringUtil.empty(item.getText())) { return; } item.getAV().setLaTeXLoaded(); createGeoFromInput(keepFocus); return; } if (!isEditing()) { return; } item.stopEditing(item.getText(), new AsyncOperation<GeoElementND>() { @Override public void callback(GeoElementND obj) { if (obj != null && !keepFocus) { if (app.has(Feature.AUTOSCROLLING_SPREADSHEET)) { app.setScrollToShow(true); } obj.update(); } } }, keepFocus); } @Override public void onEnter() { if (isSuggesting()) { sug.needsEnterForSuggestion(); return; } onEnter(true); item.getAV().clearActiveItem(); } @Override public void onKeyTyped() { item.onKeyTyped(); } @Override public void onCursorMove() { item.onCursorMove(); } @Override public void onUpKeyPressed() { if (isSuggesting()) { sug.onKeyUp(); } } @Override public void onDownKeyPressed() { if (isSuggesting()) { sug.onKeyDown(); } } @Override public String alt(int unicodeKeyChar, boolean shift) { return getRetexListener().alt(unicodeKeyChar, shift); } @Override public String serialize(MathSequence selectionText) { return GeoGebraSerializer.serialize(selectionText); } @Override public void onInsertString() { getMathField().setFormula( GeoGebraSerializer.reparse(getMathField().getFormula())); } /** * @return whether suggestions are open */ public boolean isSuggesting() { return sug != null && sug.isSuggesting(); } private void createGeoFromInput(final boolean keepFocus) { String newValue = item.getText(); final String rawInput = app.getKernel().getInputPreviewHelper() .getInput(newValue); boolean textInput = isInputAsText(); final String input = textInput ? "\"" + rawInput + "\"": rawInput; setInputAsText(false); final boolean valid = input.equals(newValue); app.setScrollToShow(true); final int oldStep = app.getKernel().getConstructionStep(); AsyncOperation<GeoElementND[]> callback = new AsyncOperation<GeoElementND[]>() { @Override public void callback(GeoElementND[] geos) { if (geos == null) { // inputField.getTextBox().setFocus(true); setFocus(true); return; } // need label if we type just eg // lnx if (geos.length == 1 && !geos[0].isLabelSet()) { geos[0].setLabel(geos[0].getDefaultLabel()); } InputHelper.updateProperties(geos, app.getActiveEuclidianView(), oldStep); app.setScrollToShow(false); /** * if (!valid) { addToHistory(input, null); * addToHistory(newValueF, latexx); } else { addToHistory(input, * latexx); } */ Scheduler.get() .scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { item.scrollIntoView(); if (keepFocus) { setFocus(true); } else { item.setFocus(false, true); } } }); item.setText(""); item.removeOutput(); } }; // keepFocus==false: this was called from blur, don't use modal slider // dialog ErrorHandler err = null; if (!textInput) { Log.debug("GETTING HANDLER " + valid + ", " + keepFocus); err = item.getErrorHandler(valid, keepFocus); err.resetError(); } EvalInfo info = new EvalInfo(true, true).withSliders(true) .withFractions(true); app.getKernel().getAlgebraProcessor() .processAlgebraCommandNoExceptionHandling(input, true, err, info, callback); if (!keepFocus) { item.setFocus(false, false); } } /** * @param text * text to be inserted */ public void autocomplete(String text) { GuiManagerW.makeKeyboardListener(retexListener).insertString(text); } /** * @return keyboard listener */ public RetexKeyboardListener getRetexListener() { return retexListener; } /** * @param retexListener * keyboard listener */ public void setRetexListener(RetexKeyboardListener retexListener) { this.retexListener = retexListener; } /** * Coneect keyboard listener to keyboard */ public void setOnScreenKeyboardTextField() { app.getGuiManager().setOnScreenKeyboardTextField(getRetexListener()); // prevent that keyboard is closed on clicks (changing // cursor position) CancelEventTimer.keyboardSetVisible(); } @Override public void showKeyboard() { app.showKeyboard(retexListener); } /** * @param show * whether to show keyboard */ public void initAndShowKeyboard(boolean show) { retexListener = new RetexKeyboardListener(item.canvas, getMathField()); if (show) { app.getAppletFrame().showKeyBoard(true, retexListener, false); } } private MathFieldW getMathField() { return item.getMathField(); } /** * @return input suggestion model (lazy load) */ InputSuggestions getInputSuggestions() { if (sug == null) { sug = new InputSuggestions(app, item); } return sug; } /** * Prevent blur in the next 200ms */ public void preventBlur() { this.preventBlur = true; Timer t = new Timer(){ @Override public void run() { preventBlur=false; } }; t.schedule(200); } public boolean onEscape() { if (item.geo != null) { item.cancelEditing(); return true; } return false; } }