package org.geogebra.desktop.gui.inputfield;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.geogebra.common.GeoGebraConstants;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.awt.GFont;
import org.geogebra.common.awt.GGraphics2D;
import org.geogebra.common.euclidian.Drawable;
import org.geogebra.common.euclidian.draw.DrawInputBox;
import org.geogebra.common.euclidian.event.FocusListener;
import org.geogebra.common.euclidian.event.KeyHandler;
import org.geogebra.common.gui.inputfield.AutoComplete;
import org.geogebra.common.gui.inputfield.AutoCompleteTextField;
import org.geogebra.common.gui.inputfield.InputHelper;
import org.geogebra.common.javax.swing.GBox;
import org.geogebra.common.kernel.Macro;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoInputBox;
import org.geogebra.common.main.App;
import org.geogebra.common.main.Localization;
import org.geogebra.common.main.MyError;
import org.geogebra.common.util.AutoCompleteDictionary;
import org.geogebra.common.util.Korean;
import org.geogebra.common.util.StringUtil;
import org.geogebra.common.util.debug.Log;
import org.geogebra.desktop.awt.GColorD;
import org.geogebra.desktop.awt.GFontD;
import org.geogebra.desktop.euclidian.event.FocusListenerD;
import org.geogebra.desktop.euclidian.event.KeyListenerD;
import org.geogebra.desktop.gui.autocompletion.CommandCompletionListCellRenderer;
import org.geogebra.desktop.gui.autocompletion.CompletionsPopup;
import org.geogebra.desktop.gui.util.GeoGebraIconD;
import org.geogebra.desktop.main.AppD;
public class AutoCompleteTextFieldD extends MathTextField
implements AutoComplete, AutoCompleteTextField {
private static final long serialVersionUID = 1L;
private transient AppD app;
private transient Localization loc;
private StringBuilder curWord;
private int curWordStart;
protected AutoCompleteDictionary dict;
protected boolean isCASInput = false;
protected boolean autoComplete;
private int historyIndex;
private ArrayList<String> history;
private KeyNavigation handleEscapeKey = KeyNavigation.IGNORE;
private List<String> completions;
private String cmdPrefix;
private CompletionsPopup completionsPopup;
private HistoryPopupD historyPopup;
private DrawInputBox drawTextField = null;
/**
* Flag to determine if text must start with "=" to activate autoComplete;
* used with spreadsheet cells
*/
private boolean isEqualsRequired = false;
private boolean popupSymbolDisabled = false;
private boolean forCAS;
/**
* Pattern to find an argument description as found in the syntax
* information of a command.
*/
// private static Pattern syntaxArgPattern =
// Pattern.compile("[,\\[] *(?:<[\\(\\) \\-\\p{L}]*>|\\.\\.\\.)
// *(?=[,\\]])");
// Simplified to this as there are too many non-alphabetic character in
// parameter descriptions:
private static Pattern syntaxArgPattern = Pattern
.compile("[,\\[\\(] *(?:<.*?>|\"<.*?>\"|\\.\\.\\.) *(?=[,\\]\\)])");
/**
* Constructs a new AutoCompleteTextField that uses the dictionary of the
* given Application for autocomplete look up. A default model is created
* and the number of columns is 0.
*
*/
public AutoCompleteTextFieldD(int columns, App app) {
this(columns, (AppD) app, KeyNavigation.BLUR);
}
/**
* @param columns
* width
* @param app
* Application
* @param handleEscapeKey
* how to handle escape key
* @param forCAS
* dictionary
*/
public AutoCompleteTextFieldD(int columns, AppD app,
KeyNavigation handleEscapeKey, boolean forCAS) {
super(app);
// allow dynamic width with columns = -1
if (columns > 0) {
setColumns(columns);
}
this.app = app;
this.loc = app.getLocalization();
setAutoComplete(true);
this.handleEscapeKey = handleEscapeKey;
curWord = new StringBuilder();
historyIndex = 0;
history = new ArrayList<String>(50);
completions = null;
CommandCompletionListCellRenderer cellRenderer = new CommandCompletionListCellRenderer();
completionsPopup = new CompletionsPopup(this, cellRenderer, 6);
// addKeyListener(this); now in MathTextField
setDictionary(forCAS);
enableLabelColoring(isCASInput);
}
public AutoCompleteTextFieldD(int columns, AppD app,
KeyNavigation handleEscapeKey) {
this(columns, app, handleEscapeKey, true);
// setDictionary(app.getAllCommandsDictionary());
}
public AutoCompleteTextFieldD(int columns, App app,
Drawable drawTextField) {
this(columns, app);
this.drawTextField = (DrawInputBox) drawTextField;
}
@Override
public DrawInputBox getDrawTextField() {
return drawTextField;
}
public ArrayList<String> getHistory() {
return history;
}
/**
* Add a history popup list and an embedded popup button. See
* AlgebraInputBar
*/
public void addHistoryPopup(boolean isDownPopup) {
if (historyPopup == null) {
historyPopup = new HistoryPopupD(this);
}
historyPopup.setDownPopup(isDownPopup);
ActionListener al = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals(1 + BorderButtonD.cmdSuffix)) {
// TODO: should up/down orientation be tied to InputBar?
// show popup
historyPopup.showPopup();
}
}
};
setBorderButton(1, GeoGebraIconD.createUpDownTriangleIcon(false, true),
al);
this.setBorderButtonVisible(1, false);
}
@Override
public void showPopupSymbolButton(boolean showPopupSymbolButton) {
((MyTextFieldD) this).setShowSymbolTableIcon(
showPopupSymbolButton && !popupSymbolDisabled);
}
@Override
public void removeSymbolTable() {
popupSymbolDisabled = true;
}
/**
* Set whether this is a CAS Input field
*/
public void setCASInput(boolean val) {
isCASInput = val;
enableLabelColoring(isCASInput);
}
/**
* Set the dictionary that autocomplete lookup should be performed by.
*
* @param forCAS
* whether this is for CAS
*/
@Override
public void setDictionary(boolean forCAS) {
this.dict = null;
this.forCAS = forCAS;
}
/**
* Gets the dictionary currently used for lookups.
*
* @return dict The dictionary that will be used for the autocomplete
* lookups.
*/
@Override
public AutoCompleteDictionary getDictionary() {
if (this.dict == null) {
this.dict = this.forCAS ? app.getCommandDictionaryCAS()
: app.getCommandDictionary();
}
return this.dict;
}
/**
* Sets whether the component is currently performing autocomplete lookups
* as keystrokes are performed.
*
* @param val
* True or false.
*/
@Override
public void setAutoComplete(boolean val) {
autoComplete = val && app.getLocalization().isAutoCompletePossible();
if (autoComplete) {
app.initTranslatedCommands();
}
}
public List<String> getCompletions() {
return completions;
}
/**
* Gets whether the component is currently performing autocomplete lookups
* as keystrokes are performed.
*
* @return True or false.
*/
@Override
public boolean getAutoComplete() {
return autoComplete && loc.isAutoCompletePossible();
}
public String getCurrentWord() {
return curWord.toString();
}
public int getCurrentWordStart() {
return curWordStart;
}
@Override
public void geoElementSelected(GeoElement geo, boolean add) {
if (geo != null) {
replaceSelection(
" " + geo.getLabel(StringTemplate.defaultTemplate) + " ");
requestFocusInWindow();
}
}
/** returns if text must start with "=" to activate autocomplete */
public boolean isEqualsRequired() {
return isEqualsRequired;
}
/** sets flag to require text starts with "=" to activate autocomplete */
public void setEqualsRequired(boolean isEqualsRequired) {
this.isEqualsRequired = isEqualsRequired;
}
// ----------------------------------------------------------------------------
// Protected methods
// ----------------------------------------------------------------------------
boolean ctrlC = false;
private GeoInputBox geoUsedForInputBox;
private boolean previewActive = true;
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
// we don't want to trap AltGr
// as it is used eg for entering {[}] is some locales
// NB e.isAltGraphDown() doesn't work
if (e.isAltDown() && e.isControlDown()) {
return;
}
// swallow eg ctrl-a ctrl-b ctrl-p on Mac
if (AppD.MAC_OS && e.isControlDown()) {
e.consume();
}
ctrlC = false;
switch (keyCode) {
case KeyEvent.VK_Z:
case KeyEvent.VK_Y:
if (AppD.isControlDown(e)) {
app.getGlobalKeyDispatcher().handleGeneralKeys(e);
e.consume();
}
break;
case KeyEvent.VK_C:
if (AppD.isControlDown(e)) // workaround for MAC_OS
{
ctrlC = true;
}
break;
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
if (AppD.isControlDown(e) && e.isShiftDown()) {
app.getGlobalKeyDispatcher().handleGeneralKeys(e);
}
break;
// process input
case KeyEvent.VK_ESCAPE:
if (handleEscapeKey == KeyNavigation.IGNORE) {
break;
}
if (handleEscapeKey == KeyNavigation.HISTORY) {
setText("");
}
Component comp = SwingUtilities.getRoot(this);
if (comp instanceof JDialog) {
((JDialog) comp).setVisible(false);
return;
}
// loose focus
app.getActiveEuclidianView().requestFocusInWindow();
break;
// removed - what is this for?
// case KeyEvent.VK_LEFT_PARENTHESIS:
// break;
case KeyEvent.VK_UP:
if (handleEscapeKey == KeyNavigation.IGNORE) {
break;
}
if (historyPopup == null) {
String text = getPreviousInput();
if (text != null) {
setText(text);
}
} else if (!historyPopup.isDownPopup()) {
historyPopup.showPopup();
}
break;
case KeyEvent.VK_DOWN:
if (handleEscapeKey == KeyNavigation.IGNORE) {
break;
}
if (historyPopup != null && historyPopup.isDownPopup()) {
historyPopup.showPopup();
} else {
// Fix for Ticket #463
if (getNextInput() != null) {
setText(getNextInput());
}
}
break;
case KeyEvent.VK_F9:
// needed for applets
if (app.isApplet()) {
app.getGlobalKeyDispatcher().handleGeneralKeys(e);
}
break;
case KeyEvent.VK_RIGHT:
if (moveToNextArgument(false)) {
e.consume();
}
break;
case KeyEvent.VK_TAB:
if (usedForInputBox()) {
AutoCompleteTextField tf = app.getActiveEuclidianView()
.getTextField();
if (tf != null) {
geoUsedForInputBox.setText(tf.getText());
}
//
// app.getGlobalKeyDispatcher().handleTab(e.isControlDown(),
// e.isShiftDown(), true);
app.getGlobalKeyDispatcher().handleGeneralKeys(e);
GeoElement next = app.getSelectionManager().getSelectedGeos()
.get(0);
Log.debug("next is " + next);
if (next instanceof GeoInputBox) {
GeoInputBox input = (GeoInputBox) next;
app.getActiveEuclidianView().focusTextField(input);
} else {
// app.getActiveEuclidianView().requestFocus();
}
//
} else if (moveToNextArgument(true)) {
e.consume();
}
break;
case KeyEvent.VK_F1:
String helpURL = isCASInput ? App.WIKI_CAS_VIEW : App.WIKI_MANUAL;
if (autoComplete) {
boolean commandFound = false;
if (!getText().equals("")) {
int pos = getCaretPosition();
while (pos > 0 && getText().charAt(pos - 1) == '[') {
pos--;
}
String word = getWordAtPos(getText(), pos);
String lowerCurWord = word.toLowerCase();
String closest = getDictionary().lookup(lowerCurWord);
if (closest != null) {// &&
// lowerCurWord.equals(closest.toLowerCase()))
showCommandHelp(app.getInternalCommand(closest),
isCASInput);
commandFound = true;
}
}
if (!commandFound) {
Object[] options = { loc.getPlain("OK"),
loc.getPlain("ShowOnlineHelp") };
int n = JOptionPane.showOptionDialog(app.getMainComponent(),
loc.getMenu(isCASInput ? "CASFieldHelp"
: "InputFieldHelp"),
GeoGebraConstants.APPLICATION_NAME + " - "
+ loc.getMenu("Help"),
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE, null, // do not use a
// custom Icon
options, // the titles of buttons
options[0]); // default button title
if (n == 1) {
app.getGuiManager().openHelp(helpURL);
}
}
} else {
app.getGuiManager().openHelp(helpURL);
}
e.consume();
break;
default:
}
}
@Override
public void keyReleased(KeyEvent e) {
// stop autocompletion re-opening on <Escape>
if (e.getKeyCode() == KeyEvent.VK_ESCAPE
|| e.getKeyCode() == KeyEvent.VK_RIGHT
|| e.getKeyCode() == KeyEvent.VK_LEFT
|| e.getKeyCode() == KeyEvent.VK_ENTER) {
e.consume();
return;
}
// Application.debug(e+"");
/*
* test code to generate unicode strings for Virtual Keyboard String
* text = getText(); String outStr = ""; for (int i = 0 ; i <
* text.length() ; i++) { int ch = text.charAt(i); if (ch < 128) outStr
* += text.charAt(i); else { String unicode = Integer.toHexString(ch);
* if (unicode.length() < 4) unicode = "\\u0"+unicode; else unicode =
* "\\u"+unicode; outStr += unicode; } } Application.debug(outStr); //
*/
// ctrl pressed on Mac
// or alt on Windows
boolean modifierKeyPressed = AppD.MAC_OS ? e.isControlDown()
: e.isAltDown();
// we don't want to act when AltGr is down
// as it is used eg for entering {[}] is some locales
// NB e.isAltGraphDown() doesn't work
if (e.isAltDown() && e.isControlDown()) {
modifierKeyPressed = false;
}
char charPressed = e.getKeyChar();
if ((StringUtil.isLetterOrDigitOrUnderscore(charPressed)
|| modifierKeyPressed)
&& !(ctrlC && (AppD.MAC_OS || AppD.LINUX))
&& !(e.getKeyCode() == KeyEvent.VK_A && AppD.MAC_OS)) {
clearSelection();
}
// handle alt-p etc
super.keyReleased(e);
mergeKoreanDoubles();
if (getAutoComplete()) {
updateCurrentWord(false);
startAutoCompletion();
}
/*
* if (charCodePressed == KeyEvent.VK_BACK_SPACE && isTextSelected &&
* input.length() > 0) { setText(input.substring(0, input.length())); }
*/
}
public void mergeKoreanDoubles() {
// avoid shift on Korean keyboards
if (app.getLocale().getLanguage().equals("ko")) {
String text = getText();
int caretPos = getCaretPosition();
String mergeText = Korean.mergeDoubleCharacters(text);
int decrease = text.length() - mergeText.length();
if (decrease > 0) {
setText(mergeText);
setCaretPosition(caretPos - decrease);
}
}
}
private void clearSelection() {
int start = getSelectionStart();
int end = getSelectionEnd();
// clear selection if there is one
if (start != end) {
int pos = getCaretPosition();
String oldText = getText();
StringBuilder sb = new StringBuilder();
sb.append(oldText.substring(0, start));
sb.append(oldText.substring(end));
setText(sb.toString());
// set caret position to start
pos = start;
if (pos < sb.length()) {
setCaretPosition(pos);
}
}
}
/**
* Automatically closes parentheses (, {, [ when next sign is a space or end
* of input text. and ignores ] }, ) if the brackets already match (simple
* check)
*/
@Override
public void keyTyped(KeyEvent e) {
// only handle parentheses
char ch = e.getKeyChar();
int caretPos = getCaretPosition();
String text = getText();
// checking for isAltDown() because Alt+, prints another character on
// the PC
// TODO make this more robust - perhaps it could go in a document change
// listener
if (ch == ',' && !e.isAltDown()) {
if (caretPos < text.length() && text.charAt(caretPos) == ',') {
// User typed ',' just in ahead of an existing ',':
// We may be in the position of filling in the next argument of
// an autocompleted command
// Look for a pattern of the form ", < Argument Description > ,"
// or ", < Argument Description > ]"
// If found, select the argument description so that it can
// easily be typed over with the value
// of the argument.
if (moveToNextArgument(false)) {
e.consume();
}
return;
}
}
if (!(ch == '(' || ch == '{' || ch == '[' || ch == '}' || ch == ')'
|| ch == ']')) {
super.keyTyped(e);
return;
}
clearSelection();
caretPos = getCaretPosition();
if (ch == '}' || ch == ')' || ch == ']') {
// simple check if brackets match
if (text.length() > caretPos && text.charAt(caretPos) == ch) {
int count = 0;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c == '{') {
count++;
} else if (c == '}') {
count--;
} else if (c == '(') {
count += 1E3;
} else if (c == ')') {
count -= 1E3;
} else if (c == '[') {
count += 1E6;
} else if (c == ']') {
count -= 1E6;
}
}
if (count == 0) {
// if brackets match, just move the cursor forwards one
e.consume();
caretPos++;
}
}
}
// auto-close parentheses
if (!e.isAltDown() && (caretPos == text.length()
|| org.geogebra.common.gui.inputfield.MyTextField
.isCloseBracketOrWhitespace(text.charAt(caretPos)))) {
this.setPreviewActive(false);
switch (ch) {
default:
// do nothing
break;
case '(':
// opening parentheses: insert closing parenthesis automatically
insertString(")");
break;
case '{':
// opening braces: insert closing parenthesis automatically
insertString("}");
break;
case '[':
// opening bracket: insert closing parenthesis automatically
insertString("]");
break;
}
this.setPreviewActive(true);
}
// make sure we keep the previous caret position
setCaretPosition(Math.min(text.length(), caretPos));
}
private void setPreviewActive(boolean b) {
previewActive = b;
}
/**
* Updates curWord to word at current caret position. curWordStart,
* curWordEnd are set to this word's start and end position
*/
public void updateCurrentWord(boolean searchRight) {
int next = InputHelper.updateCurrentWord(searchRight, this.curWord,
getText(), getCaretPosition(), true);
if (next > -1) {
this.curWordStart = next;
}
}
// returns the word at position pos in text
public static String getWordAtPos(String text, int pos) {
// search to the left
int wordStart = pos - 1;
while (wordStart >= 0 && StringUtil
.isLetterOrDigitOrUnderscore(text.charAt(wordStart))) {
--wordStart;
}
wordStart++;
// search to the right
int wordEnd = pos;
int length = text.length();
while (wordEnd < length
&& StringUtil.isLetterOrDigitOrUnderscore(text.charAt(wordEnd))) {
++wordEnd;
}
if (wordStart >= 0 && wordEnd <= length) {
return text.substring(wordStart, wordEnd);
}
return null;
}
// static String lastTyped = null;
/*
* ----------------------------------------- Autocompletion
* -----------------------------------------
*/
private boolean moveToNextArgument(boolean find) {
String text = getText();
int caretPos = getCaretPosition();
// make sure it works if caret is just after [
// if (caretPos > 0 && text.charAt(caretPos - 1) == '[') caretPos--;
Matcher argMatcher = syntaxArgPattern.matcher(text);
boolean hasNextArgument = argMatcher.find(caretPos);
if (find && !hasNextArgument) {
hasNextArgument = argMatcher.find();
}
if (hasNextArgument && (find || argMatcher.start() == caretPos)) {
setCaretPosition(argMatcher.end());
// do not select the space after , but do select space after [
if (text.charAt(argMatcher.start()) == ',') {
moveCaretPosition(argMatcher.start() + 2);
} else {
moveCaretPosition(argMatcher.start() + 1);
}
return true;
}
return false;
}
private List<String> resetCompletions() {
String text = getText();
updateCurrentWord(false);
completions = null;
if (isEqualsRequired && !text.startsWith("=")) {
return null;
}
boolean korean = app.getLocale().getLanguage().equals("ko");
// start autocompletion only for long enough words
if (!InputHelper.needsAutocomplete(curWord, app.getKernel())) {
completions = null;
return null;
}
cmdPrefix = curWord.toString();
if (korean) {
completions = getDictionary().getCompletionsKorean(cmdPrefix);
} else {
completions = getDictionary().getCompletions(cmdPrefix);
}
List<String> commandCompletions = getSyntaxes(completions);
// Start with the built-in function completions
completions = app.getParserFunctions().getCompletions(cmdPrefix);
// Then add the command completions
if (completions.isEmpty()) {
completions = commandCompletions;
} else if (commandCompletions != null) {
completions.addAll(commandCompletions);
}
return completions;
}
/*
* Take a list of commands and return all possible syntaxes for these
* commands
*/
private List<String> getSyntaxes(List<String> commands) {
if (commands == null) {
return null;
}
ArrayList<String> syntaxes = new ArrayList<String>();
for (String cmd : commands) {
String cmdInt = app.getInternalCommand(cmd);
String syntaxString;
String suffix = Localization.syntaxStr;
if (isCASInput) {
syntaxString = loc.getCommandSyntaxCAS(cmdInt);
if (syntaxString.endsWith(Localization.syntaxCAS)) {
syntaxString = loc.getCommandSyntax(cmdInt);
} else {
suffix = Localization.syntaxCAS;
}
} else {
syntaxString = loc.getCommandSyntax(cmdInt);
}
if (syntaxString.endsWith(suffix)) {
// command not found, check for macros
Macro macro = isCASInput ? null : app.getKernel().getMacro(cmd);
if (macro != null) {
syntaxes.add(macro.toString());
} else {
// syntaxes.add(cmdInt + "[]");
Log.debug("Can't find syntax for: " + cmd);
}
continue;
}
for (String syntax : syntaxString.split("\\n")) {
syntaxes.add(syntax);
}
}
return syntaxes;
}
public void startAutoCompletion() {
// don't show autocompletion popup if the current word
// is a defined variable
resetCompletions();
completionsPopup.showCompletions();
}
public void cancelAutoCompletion() {
completions = null;
}
/**
* Ticket #1167 Auto-completes input; <br>
*
* @param index
* index of the chosen command in the completions list
* @param completions
* @return false if completions list is null or index < 0 or index >
* completions.size()
* @author Arnaud
*/
public boolean validateAutoCompletion(int index, List<String> completions) {
if (completions == null || index < 0 || index >= completions.size()) {
return false;
}
int start = curWordStart;
String command = completions.get(index);
String text = getText();
String before = text.substring(0, curWordStart);
String after = text.substring(curWordStart + curWord.length());
int bracketIndex = command.indexOf('[');
if (bracketIndex == -1) {
bracketIndex = command.indexOf('(');
}
if (bracketIndex > -1 && after.startsWith("[")) {
// probably already have some arguments
// eg user is just changing the command name
command = command.substring(0, bracketIndex);
}
StringBuilder sb = new StringBuilder();
sb.append(before);
sb.append(command);
sb.append(after);
setText(sb.toString());
// Special case if the completion is a built-in function
if (bracketIndex == -1) {
bracketIndex = command.indexOf('(');
setCaretPosition(start + bracketIndex + 1);
return true;
}
if (command.indexOf("[]") > -1) {
// eg GetTime[]
bracketIndex += 2;
} else if (command.indexOf("[ ]") > -1) {
// eg GetTime[ ]
bracketIndex += 3;
}
setCaretPosition(start + bracketIndex);
moveToNextArgument(false);
return true;
}
/**
* Adds string to input textfield's history
*
* @param str
*/
public void addToHistory(String str) {
// exit if the new string is the same as the last entered string
if (!history.isEmpty() && str.equals(history.get(history.size() - 1))) {
return;
}
history.add(str);
historyIndex = history.size();
if (historyPopup != null && !isBorderButtonVisible(1)) {
setBorderButtonVisible(1, true);
}
}
/**
* @return previous input from input textfield's history
*/
private String getPreviousInput() {
if (history.size() == 0) {
return null;
}
if (historyIndex > 0) {
--historyIndex;
}
return history.get(historyIndex);
}
/**
* @return next input from input textfield's history
*/
private String getNextInput() {
if (historyIndex < history.size()) {
++historyIndex;
}
if (historyIndex == history.size()) {
return null;
}
return history.get(historyIndex);
}
/**
* shows dialog with syntax info
*
* @param cmd
* is the internal command name
*/
private void showCommandHelp(String cmd, boolean cas) {
// show help for current command (current word)
String help = cas ? loc.getCommandSyntaxCAS(cmd)
: loc.getCommandSyntax(cmd);
// show help if available
if (help != null) {
app.showError(new MyError(loc,
loc.getPlain("Syntax") + ":\n" + help, cmd, null));
} else {
app.getGuiManager().openCommandHelp(null);
}
}
/**
* just show syntax error (already correctly formulated by
* CommandProcessor.argErr())
*
* @param e
* error
*/
public void showError(MyError e) {
app.showError(e);
}
@Override
public void setFont(GFont font) {
super.setFont(GFontD.getAwtFont(font));
}
/**
* Set the font of completions and history commands
*
* @param font
* the new font
*/
public void setPopupsFont(Font font) {
if (this.completionsPopup != null) {
this.completionsPopup.setFont(font);
}
if (this.historyPopup != null) {
this.historyPopup.setFont(font);
}
}
@Override
public void setForeground(GColor color) {
super.setForeground(GColorD.getAwtColor(color));
}
@Override
public void setBackground(GColor color) {
super.setBackground(GColorD.getAwtColor(color));
}
@Override
public void addFocusListener(FocusListener focusListener) {
if (focusListener instanceof FocusListenerD) {
super.addFocusListener((FocusListenerD) focusListener);
}
}
@Override
public void wrapSetText(final String s) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setText(s);
}
});
}
@Override
public void setUsedForInputBox(GeoInputBox geo) {
geoUsedForInputBox = geo;
}
@Override
public boolean usedForInputBox() {
return geoUsedForInputBox != null;
}
@Override
public void requestFocus() {
super.requestFocus();
if (geoUsedForInputBox != null && !geoUsedForInputBox.isSelected()) {
app.getSelectionManager().clearSelectedGeos(false);
app.getSelectionManager().addSelectedGeo(geoUsedForInputBox);
}
}
@Override
public void addKeyHandler(KeyHandler handler) {
addKeyListener(new KeyListenerD(handler));
}
@Override
public String getCommand() {
this.updateCurrentWord(true);
return this.getCurrentWord();
}
@Override
public void setFocus(boolean b) {
// called from common, needed only in web
}
@Override
public void prepareShowSymbolButton(boolean b) {
this.setShowSymbolTableIcon(b);
}
@Override
public void drawBounds(GGraphics2D g2, GColor bgColor, int left, int top,
int width, int height) {
g2.setPaint(bgColor);
g2.fillRect(left - 1, top - 1, width - 1, height - 4);
// TF Rectangle
g2.setPaint(GColor.LIGHT_GRAY);
g2.drawRect(left - 1, top - 1, width - 1, height - 4);
}
@Override
public void hideDeferred(final GBox box) {
setVisible(false);
box.setVisible(false);
}
/**
* @return whether we may update preview
*/
public boolean isPreviewActive() {
return previewActive;
}
@Override
public void setDrawTextField(DrawInputBox df) {
drawTextField = df;
}
@Override
public GeoInputBox getInputBox() {
return geoUsedForInputBox;
}
}