package org.geogebra.web.web.gui.inputbar;
import org.geogebra.common.gui.inputfield.InputHelper;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.main.App;
import org.geogebra.common.main.App.InputPosition;
import org.geogebra.common.main.Feature;
import org.geogebra.common.main.GWTKeycodes;
import org.geogebra.common.main.Localization;
import org.geogebra.common.main.MyError;
import org.geogebra.common.main.error.ErrorHandler;
import org.geogebra.common.main.error.ErrorHelper;
import org.geogebra.common.util.AsyncOperation;
import org.geogebra.common.util.lang.Unicode;
import org.geogebra.web.html5.css.GuiResourcesSimple;
import org.geogebra.web.html5.gui.AlgebraInput;
import org.geogebra.web.html5.gui.GPopupPanel;
import org.geogebra.web.html5.gui.inputfield.AutoCompleteTextFieldW;
import org.geogebra.web.html5.gui.util.NoDragImage;
import org.geogebra.web.html5.javax.swing.GOptionPaneW;
import org.geogebra.web.html5.main.AppW;
import org.geogebra.web.web.css.GuiResources;
import org.geogebra.web.web.gui.GuiManagerW;
import org.geogebra.web.web.gui.layout.panels.AlgebraDockPanelW;
import org.geogebra.web.web.gui.view.algebra.AlgebraViewW;
import org.geogebra.web.web.gui.view.algebra.InputPanelW;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.ToggleButton;
/**
* @author gabor
*
* InputBar for GeoGebraWeb
*
*/
public class AlgebraInputW extends FlowPanel
implements KeyUpHandler, FocusHandler, ClickHandler, BlurHandler,
RequiresResize, AlgebraInput, HasHelpButton {
/** app */
protected AppW app;
/** input panel */
protected InputPanelW inputPanel;
/** text component */
protected AutoCompleteTextFieldW inputField;
//protected FlowPanel innerPanel;
/** button for help */
protected ToggleButton btnHelpToggle;
/** help popup */
protected InputBarHelpPopup helpPopup;
// protected PopupPanel helpPopup;
private boolean focused = false;
/**
* @param app1
* Application
*
* Attaches Application and creates the GUI of AlgebraInput
*/
public void init(AppW app1) {
this.app = app1;
//AG I dont think we need this app.removeTraversableKeys(this);
addStyleName("AlgebraInput");
if (app.has(Feature.SHOW_ONE_KEYBOARD_BUTTON_IN_FRAME)) {
addStyleName("AlgebraInput2");
}
initGUI();
app1.getGuiManager().addAlgebraInput(this);
}
/**
* Initialize the UI
*/
public void initGUI() {
clear();
inputPanel = new InputPanelW(app, 0, true);
inputField = inputPanel.getTextComponent();
inputField.requestToShowSymbolButton();
inputField.getTextBox().addKeyUpHandler(this);
inputField.getTextBox().addFocusHandler(this);
inputField.getTextBox().addBlurHandler(this);
inputField.addHistoryPopup(app.getInputPosition() == InputPosition.top);
//AG updateFonts()
updateIcons(false);
//new Image(AppResources.INSTANCE.inputhelp_right_20x20().getSafeUri().asString()));
btnHelpToggle.addStyleName("inputHelp-toggleButton");
btnHelpToggle.addClickHandler(this);
//labelPanel.setHorizontalAlignment(ALIGN_RIGHT);
//labelPanel.setVerticalAlignment(ALIGN_MIDDLE);
// TODO: eastPanel should hold the command help button
//eastPanel = new FlowPanel();
// place all components in an inner panel
//innerPanel = new FlowPanel();
//innerPanel.setCellHorizontalAlignment(labelPanel, ALIGN_RIGHT);
//innerPanel.setCellVerticalAlignment(labelPanel, ALIGN_MIDDLE);
add(inputPanel);
//innerPanel.setCellHorizontalAlignment(inputPanel, ALIGN_LEFT);
//innerPanel.setCellVerticalAlignment(inputPanel, ALIGN_MIDDLE);
//setCellVerticalAlignment(innerPanel, ALIGN_MIDDLE);
// add innerPanel to wrapper (this panel)
//setVerticalAlignment(ALIGN_MIDDLE);
//add(innerPanel);
//add(eastPanel);
//setCellVerticalAlignment(this, ALIGN_MIDDLE);
if (app.showInputHelpToggle()) {
add(btnHelpToggle);
}
setLabels();
//setInputFieldWidth();
}
private void updateIcons(boolean warning) {
if (btnHelpToggle == null) {
btnHelpToggle = new ToggleButton();
}
btnHelpToggle.getUpFace().setImage(new NoDragImage(
(warning ? GuiResourcesSimple.INSTANCE.icon_dialog_warning()
: GuiResources.INSTANCE.icon_help())
.getSafeUri()
.asString(),
24));
// new
// Image(AppResources.INSTANCE.inputhelp_left_20x20().getSafeUri().asString()),
btnHelpToggle.getDownFace().setImage(new NoDragImage(
(warning ? GuiResourcesSimple.INSTANCE.icon_dialog_warning()
: GuiResources.INSTANCE.icon_help())
.getSafeUri().asString(),
24));
}
/**
* Sets the width of the text field so that the entire width of the parent
* container is used. (Really just a workaround because the nested gwt
* panels are not allowing 100% width to work as we would like).
*/
@Override
public void setInputFieldWidth(int width) {
// if the size is too small, use default size
if (width > 100) {
inputPanel.setWidth((width - 100) + "px");
}
}
@Override
public void onResize() {
if(app == null){
return;
}
if (!app.isApplet()) {
setInputFieldWidth((int)app.getWidth());
}
// hide the help popup
// btnHelpToggle.setValue(false);
setShowInputHelpPanel(false);
}
/**
* updates labels according to current locale
*/
public void setLabels() {
if(app == null){
return;
}
Localization loc = app.getLocalization();
if (btnHelpToggle != null) {
btnHelpToggle.setTitle(loc.getMenu("InputHelp"));
}
if (helpPopup != null) {
app.getGuiManager().getInputHelpPanel().setLabels();
}
inputField.setDictionary(false);
inputField.getTextField().getElement().setAttribute("placeholder",
loc.getMenu("InputLabel") + Unicode.ellipsis);
}
/**
* Sets the content of the input textfield and gives focus to the input
* textfield.
*
* @param str
* replacement string
*/
public void replaceString(String str) {
inputField.setText(str);
}
/**
* Insert string at caret position
*
* @param str
* string to be inserted
*/
public void insertString(String str) {
if (str == null) {
return;
}
int pos = inputField.getCaretPosition();
String oldText = inputField.getText();
String newText =
oldText.substring(0, pos) + str +
oldText.substring(pos);
inputField.setText(newText);
inputField.setCaretPosition(pos + str.length());
inputField.requestFocus();
}
private AlgebraDockPanelW getAlgebraDockPanel() {
return (AlgebraDockPanelW) app.getGuiManager().getLayout()
.getDockManager().getPanel(App.VIEW_ALGEBRA);
}
@Override
public void onFocus(FocusEvent event) {
if (((AlgebraViewW) app.getGuiManager().getAlgebraView())
.isNodeTableEmpty() && !getAlgebraDockPanel().hasLongStyleBar()) {
getAlgebraDockPanel().showStyleBarPanel(false);
}
Object source = event.getSource();
AutoCompleteTextFieldW.showSymbolButtonIfExists(source, true);
app.getSelectionManager().clearSelectedGeos();
this.focused = true;
}
@Override
public void onBlur(BlurEvent event) {
getAlgebraDockPanel().showStyleBarPanel(true);
Object source = event.getSource();
AutoCompleteTextFieldW.showSymbolButtonIfExists(source, false);
this.focused = false;
onEnterPressed(false);
}
@Override
public void onKeyUp(KeyUpEvent event) {
// the input field may have consumed this event
// for auto completion
//then it don't come here if (e.isConsumed()) return;
int keyCode = event.getNativeKeyCode();
app.getKernel()
.getInputPreviewHelper()
.updatePreviewFromInputBar(inputField.getText(),
getWarningHandler(this, app));
if (keyCode == GWTKeycodes.KEY_ENTER && !inputField.isSuggestionJustHappened()) {
onEnterPressed(true);
} else if (keyCode != GWTKeycodes.KEY_C && keyCode != GWTKeycodes.KEY_V
&& keyCode != GWTKeycodes.KEY_X) {
app.getGlobalKeyDispatcher().handleGeneralKeys(event); // handle eg
// ctrl-tab
if (keyCode == GWTKeycodes.KEY_ESCAPE) {
inputField.setText(null);
}
}
inputField.setIsSuggestionJustHappened(false);
}
private void onEnterPressed(final boolean explicit) {
app.getKernel().clearJustCreatedGeosInViews();
final String input = app.getKernel().getInputPreviewHelper()
.getInput(getTextField().getText());
boolean valid = app.getKernel().getInputPreviewHelper().isValid();
if (input == null || input.length() == 0) {
app.getActiveEuclidianView().requestFocusInWindow();
return;
}
app.setScrollToShow(true);
try {
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);
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);
inputField.addToHistory(input);
if (!getTextField().getText().equals(input)) {
inputField.addToHistory(getTextField().getText());
}
inputField.setText(null);
inputField.setIsSuggestionJustHappened(false);
}
};
app.getKernel()
.getAlgebraProcessor()
.processAlgebraCommandNoExceptionHandling(input, true,
getErrorHandler(valid, explicit), true, callback);
} catch (Exception ee) {
inputField.addToHistory(getTextField().getText());
GOptionPaneW.setCaller(inputField.getTextBox());
app.showGenericError(ee);
return;
} catch (MyError ee) {
inputField.addToHistory(getTextField().getText());
GOptionPaneW.setCaller(inputField.getTextBox());
inputField.showError(ee);
return;
}
}
/**
* @param input
* input bar (plaintext or editor)
* @param app2
* app
* @return handler for preview errors
*/
public static ErrorHandler getWarningHandler(final HasHelpButton input,
final App app2) {
// TODO Auto-generated method stub
return new ErrorHandler() {
@Override
public void showError(String msg) {
input.setError(msg);
input.getHelpToggle().getElement().setTitle(msg == null
? app2.getLocalization().getMenu("InputHelp")
: msg);
}
@Override
public void resetError() {
showError(null);
}
@Override
public boolean onUndefinedVariables(String string,
AsyncOperation<String[]> callback) {
return false;
}
@Override
public void showCommandError(String command, String message) {
input.setCommandError(command);
if (((GuiManagerW) app2.getGuiManager())
.hasInputHelpPanel()) {
InputBarHelpPanelW helpPanel = ((GuiManagerW) app2
.getGuiManager()).getInputHelpPanel();
helpPanel.focusCommand(
app2.getLocalization().getCommand(command));
input.getHelpToggle().getElement().setTitle(
app2.getLocalization().getError("InvalidInput"));
}
}
@Override
public String getCurrentCommand() {
return input.getCommand();
}
};
}
@Override
public String getCommand() {
return inputField.getCommand();
}
private ErrorHandler getErrorHandler(final boolean valid,
final boolean explicit) {
return new ErrorHandler() {
@Override
public void showError(String msg) {
if (explicit) {
app.getDefaultErrorHandler().showError(msg);
}
}
@Override
public void resetError() {
showError(null);
}
@Override
public boolean onUndefinedVariables(String string,
AsyncOperation<String[]> callback) {
if (explicit) {
if (valid) {
return app.getGuiManager()
.checkAutoCreateSliders(string, callback);
} else if (app.getLocalization()
.getReverseCommand(getCurrentCommand()) != null) {
ErrorHelper.handleCommandError(app.getLocalization(),
getCurrentCommand(),
app.getDefaultErrorHandler());
return false;
}
callback.callback(new String[] { "7" });
}
return false;
}
@Override
public void showCommandError(String command, String message) {
if (explicit) {
app.getDefaultErrorHandler().showCommandError(command,
message);
}
}
@Override
public String getCurrentCommand() {
return inputField.getCommand();
}
};
}
@Override
public void requestFocus(){
inputField.requestFocus();
}
@Override
public void onClick(ClickEvent event) {
Object source = event.getSource();
if (source == btnHelpToggle) {
if (btnHelpToggle.isDown()) {
setShowInputHelpPanel(true);
} else {
setShowInputHelpPanel(false);
}
}
}
private void setHelpPopup(){
if (helpPopup == null && app != null) {
helpPopup = new InputBarHelpPopup(this.app, this.inputField,
"helpPopup");
helpPopup.addAutoHidePartner(this.getElement());
if (btnHelpToggle != null) {
helpPopup.setBtnHelpToggle(this.btnHelpToggle);
}
} else if (app != null && helpPopup.getWidget() == null) {
helpPopup.add((InputBarHelpPanelW) app.getGuiManager()
.getInputHelpPanel());
}
}
/**
*
* @param show
* whether inputhelp should be shown
*/
public void setShowInputHelpPanel(boolean show) {
if (show) {
InputBarHelpPanelW helpPanel = (InputBarHelpPanelW) app.getGuiManager()
.getInputHelpPanel();
helpPanel.updateGUI(
((GuiManagerW) app.getGuiManager()).getRootComponent()
.getOffsetHeight(),
1);
setHelpPopup();
helpPopup
.setPopupPositionAndShow(new GPopupPanel.PositionCallback() {
@Override
public void setPosition(int offsetWidth, int offsetHeight) {
helpPopup.getElement().getStyle().setProperty("left", "auto");
helpPopup.getElement().getStyle().setProperty("top", "auto");
helpPopup.getElement().getStyle().setRight(0, Unit.PX);
helpPopup.getElement().getStyle().setBottom(getOffsetHeight()*app.getArticleElement().getScaleX(), Unit.PX);
helpPopup.show();
}
});
((InputBarHelpPanelW) app.getGuiManager().getInputHelpPanel())
.focusCommand(inputField.getCommand());
} else if (helpPopup != null) {
helpPopup.hide();
}
}
@Override
public void setText(String s) {
this.inputField.setText(s);
}
/**
* @return whether this has focus
*/
public boolean hasFocus(){
return this.focused || AutoCompleteTextFieldW.isShowSymbolButtonFocused();
}
/**
* @return text field
*/
public AutoCompleteTextFieldW getTextField(){
return this.inputField;
}
@Override
public ToggleButton getHelpToggle() {
return this.btnHelpToggle;
}
public void setError(String msg) {
updateIcons(msg != null);
}
public void setCommandError(String command) {
updateIcons(true);
}
}