package org.geogebra.common.gui.inputfield;
import org.geogebra.common.euclidian.EuclidianViewInterfaceCommon;
import org.geogebra.common.kernel.CircularDefinitionException;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoText;
import org.geogebra.common.kernel.geos.HasSymbolicMode;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.main.Feature;
import org.geogebra.common.util.Korean;
import org.geogebra.common.util.StringUtil;
import org.geogebra.common.util.debug.Log;
/**
* Helper methods used by plain text and latex editors
*
*/
public class InputHelper {
/**
* @param curWord
* builder with current word
* @param kernel
* kernel
* @return whether autocomplete should open
*/
public static boolean needsAutocomplete(StringBuilder curWord,
Kernel kernel) {
if ("ko".equals(kernel.getLocalization().getLanguage())) {
if (Korean.flattenKorean(curWord.toString()).length() < 2) {
return false;
}
} else if (needsThreeLetters(kernel) && curWord.length() < 3) {
return false;
} else if (curWord.length() < 2) {
return false;
}
return kernel.lookupLabel(curWord.toString()) == null;
}
private static boolean needsThreeLetters(Kernel kernel) {
// only Simplified chinese; Traditional is using english commands
return !"zh_CN".equals(kernel.getLocalization().getLocaleStr());
}
/**
* @param geos
* geos created from input
* @param ev
* view
* @param oldStep
* cons step before update
*/
public static void updateProperties(GeoElementND[] geos,
EuclidianViewInterfaceCommon ev, int oldStep) {
// create texts in the middle of the visible view
// we must check that size of geos is not 0 (ZoomIn, ZoomOut, ...)
if (geos == null) {
return;
}
if (geos.length > 0
&& geos[0].getKernel().getConstructionStep() <= oldStep) {
return;
}
for (int i = 0; i < geos.length; i++) {
updateSymbolicMode(geos[i]);
if (geos[i] instanceof GeoText) {
GeoText text = (GeoText) geos[i];
centerText(text, ev);
}
}
}
/**
* Sets the symbolic mode of the geo according to defaults (false for simple
* fraction, true otherwise)
*
* @param geo
* geo element
*/
public static void updateSymbolicMode(GeoElementND geo) {
if (geo instanceof HasSymbolicMode) {
// start with numeric mode for simple fractions like 7/2
if (geo instanceof GeoNumeric && geo.getDefinition() != null
&& geo.getDefinition().isSimpleFraction()) {
((HasSymbolicMode) geo).setSymbolicMode(false, false);
} else {
((HasSymbolicMode) geo).setSymbolicMode(true,
geo instanceof GeoText);
}
((HasSymbolicMode) geo).updateRepaint();
}
}
/**
* @param text
* text
* @param ev
* view to use for centering
*/
public static void centerText(GeoText text,
EuclidianViewInterfaceCommon ev) {
text.setAuxiliaryObject(false);
Construction cons = text.getConstruction();
boolean absoluteTexts = cons.getApplication()
.has(Feature.ABSOLUTE_TEXTS);
if ((!text.isTextCommand() || absoluteTexts)
&& text.getStartPoint() == null) {
boolean oldSuppressLabelsStatus = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
GeoPoint p = new GeoPoint(text.getConstruction(), null,
(ev.getXmin() + ev.getXmax()) / 2,
(ev.getYmin() + ev.getYmax()) / 2, 1.0);
cons.setSuppressLabelCreation(oldSuppressLabelsStatus);
try {
if (absoluteTexts) {
text.setAbsoluteScreenLoc(ev.toScreenCoordX(p.getX()),
ev.toScreenCoordY(p.getY()));
text.setAbsoluteScreenLocActive(true);
} else {
text.setStartPoint(p);
}
text.update();
} catch (CircularDefinitionException e1) {
e1.printStackTrace();
}
}
}
private static boolean isOpenBracket(char c, boolean onlySquare) {
return c == '[' || (!onlySquare && c == '{')
|| (!onlySquare && c == '(');
}
private static boolean isCloseBracket(char c, boolean onlySquare) {
return c == ']' || (!onlySquare && c == '}')
|| (!onlySquare && c == ')');
}
/**
* @param searchRight
* whether to search right
* @param curWord
* current word builder (will be changed)
* @param text
* whole editor input
* @param caretPos0
* caret position, 0 based
* @param onlySquareBrackets
* flag to only skip [], for desktop compatibility TODO not
* needed?
* @return word start position
*/
public static int updateCurrentWord(boolean searchRight,
StringBuilder curWord, String text, int caretPos0,
boolean onlySquareBrackets) {
int caretPos = caretPos0;
int curWordStart;
if (text == null) {
return -1;
}
if (searchRight) {
// search to right first to see if we are inside [ ]
boolean insideBrackets = false;
curWordStart = caretPos;
while (curWordStart < text.length()) {
char c = text.charAt(curWordStart);
if (isOpenBracket(c, onlySquareBrackets)) {
break;
}
if (isCloseBracket(c, onlySquareBrackets)) {
insideBrackets = true;
}
curWordStart++;
}
// found [, so go back until we get a ]
if (insideBrackets) {
while (caretPos > 0 && !isOpenBracket(text.charAt(caretPos),
onlySquareBrackets)) {
caretPos--;
}
}
}
// search to the left
curWordStart = caretPos - 1;
while (curWordStart >= 0 && (curWordStart >= text.length() || StringUtil
.isLetterOrDigitOrUnderscore(text.charAt(curWordStart)))) {
--curWordStart;
}
curWordStart++;
// search to the right
int curWordEnd = caretPos;
int length = text.length();
while (curWordEnd < length && StringUtil
.isLetterOrDigitOrUnderscore(text.charAt(curWordEnd))) {
++curWordEnd;
}
curWord.setLength(0);
if (curWordEnd <= length) {
curWord.append(text.substring(curWordStart, curWordEnd));
} else {
Log.debug("CARET OUTSIDE");
}
// remove '[' at end
if (curWord.length() > 0 && isOpenBracket(
curWord.charAt(curWord.length() - 1), onlySquareBrackets)) {
curWord.setLength(curWord.length() - 1);
}
return curWordStart;
}
}