/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. */ package org.geogebra.desktop.gui.inputbar; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Point; import java.awt.SystemColor; import java.awt.dnd.DropTarget; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JToggleButton; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.geogebra.common.gui.SetLabels; import org.geogebra.common.main.App.InputPosition; 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.desktop.gui.GuiManagerD; import org.geogebra.desktop.gui.inputfield.AutoCompleteTextFieldD; import org.geogebra.desktop.gui.view.algebra.AlgebraInputDropTargetListener; import org.geogebra.desktop.gui.view.algebra.InputPanelD; import org.geogebra.desktop.main.AppD; import org.geogebra.desktop.main.LocalizationD; import org.geogebra.desktop.util.GuiResourcesD; /** * @author Markus Hohenwarter */ public class AlgebraInputD extends JPanel implements ActionListener, KeyListener, FocusListener, SetLabels, MouseListener { private static final long serialVersionUID = 1L; protected AppD app; // autocompletion text field protected AutoCompleteTextFieldD inputField; private JLabel inputLabel; private JToggleButton btnHelpToggle; private InputPanelD inputPanel; private LocalizationD loc; private String autoInput; /*********************************************************** * creates new AlgebraInput * * @param app */ public AlgebraInputD(AppD app) { this.app = app; this.loc = app.getLocalization(); app.removeTraversableKeys(this); initGUI(); addMouseListener(this); } private void addPreviewListener() { inputPanel.getTextComponent().getDocument() .addDocumentListener(new DocumentListener() { @Override public void changedUpdate(DocumentEvent e) { preview(); } @Override public void removeUpdate(DocumentEvent e) { preview(); } @Override public void insertUpdate(DocumentEvent e) { preview(); } public void preview() { if (!inputField.isPreviewActive()) { return; } setAutoInput(null); inputField.setBackground(Color.WHITE); app.getKernel().getInputPreviewHelper() .updatePreviewFromInputBar(inputField.getText(), new ErrorHandler() { @Override public void resetError() { showError(null); } @Override public void showError(String msg) { updateIcons(msg != null); btnHelpToggle.setToolTipText( msg == null ? loc.getMenu( "InputHelp") : msg); } @Override public void showCommandError( String command, String message) { updateIcons(true); if (((GuiManagerD) app .getGuiManager()) .hasInputHelpPanel()) { InputBarHelpPanelD helpPanel = (InputBarHelpPanelD) ((GuiManagerD) app .getGuiManager()) .getInputHelpPanel(); helpPanel.focusCommand(app .getLocalization() .getCommand( command)); btnHelpToggle .setToolTipText( loc.getError( "InvalidInput")); } } @Override public String getCurrentCommand() { return inputField.getCommand(); } @Override public boolean onUndefinedVariables( String string, AsyncOperation<String[]> callback) { return false; } }); } }); } @SuppressWarnings("serial") public void initGUI() { removeAll(); inputLabel = new JLabel(); inputPanel = new InputPanelD(null, app, 30, true); addPreviewListener(); // create and set up the input field inputField = (AutoCompleteTextFieldD) inputPanel.getTextComponent(); inputField.setEditable(true); inputField.addKeyListener(this); inputField.addFocusListener(this); // enable a history popup and embedded button inputField.addHistoryPopup(app.getInputPosition() == InputPosition.top); // enable drops inputField.setDragEnabled(true); inputField.setDropTarget(new DropTarget(this, new AlgebraInputDropTargetListener(app, inputField))); inputField.setColoringLabels(true); updateFonts(); // create toggle button to hide/show the input help panel btnHelpToggle = new JToggleButton() { @Override public Point getToolTipLocation(MouseEvent e) { // make sure tooltip doesn't cover button (when window // maximized) return new Point(0, (int) -this.getSize().getHeight() / 2); } }; updateIcons(false); btnHelpToggle.addActionListener(this); btnHelpToggle.setFocusable(false); btnHelpToggle.setContentAreaFilled(false); btnHelpToggle.setBorderPainted(false); // create sub-panels JPanel labelPanel = new JPanel(new BorderLayout()); labelPanel.add(inputLabel, loc.borderEast()); JPanel eastPanel = new JPanel(new BorderLayout()); if (app.showInputHelpToggle()) { eastPanel.add(btnHelpToggle, loc.borderWest()); } labelPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 2)); eastPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); inputPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0)); setLayout(new BorderLayout(0, 0)); add(labelPanel, loc.borderWest()); add(inputPanel, BorderLayout.CENTER); add(eastPanel, loc.borderEast()); setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, SystemColor.controlShadow)); setLabels(); } private void updateIcons(boolean warning) { if (btnHelpToggle == null) { return; } btnHelpToggle.setIcon(app.getScaledIconCommon(warning ? GuiResourcesD.DIALOG_ERROR : GuiResourcesD.MENU_HELP)); } @Override public boolean requestFocusInWindow() { return inputField.requestFocusInWindow(); } @Override public void requestFocus() { requestFocusInWindow(); } @Override public boolean hasFocus() { return inputField.hasFocus(); } public void clear() { inputField.setText(null); } public AutoCompleteTextFieldD getTextField() { return inputField; } public void updateOrientation(boolean showInputTop) { inputField.setOpenSymbolTableUpwards(!showInputTop); } /** * updates labels according to current locale */ @Override public void setLabels() { if (inputLabel != null) { inputLabel.setText(loc.getMenu("InputLabel") + ":"); } if (btnHelpToggle != null) { btnHelpToggle.setToolTipText(loc.getMenu("InputHelp")); } inputField.setDictionary(false); inputField.setLabels(); } public void updateFonts() { inputField.setFont(app.getBoldFont()); inputLabel.setFont(app.getPlainFont()); inputField.setPopupsFont(app.getPlainFont()); // update the help panel if (((GuiManagerD) app.getGuiManager()).hasInputHelpPanel()) { InputBarHelpPanelD helpPanel = (InputBarHelpPanelD) ((GuiManagerD) app .getGuiManager()).getInputHelpPanel(); helpPanel.updateFonts(); } updateIcons(false); } // /** // * Inserts string at current position of the input textfield and gives // focus // * to the input textfield. // * @param str: inserted string // */ // public void insertString(String str) { // inputField.replaceSelection(str); // } /** * Sets the content of the input textfield and gives focus to the input * textfield. * * @param str */ public void replaceString(String str) { inputField.setText(str); } // see actionPerformed public void insertCommand(String cmd) { if (cmd == null) { return; } int pos = inputField.getCaretPosition(); String oldText = inputField.getText(); String newText = oldText.substring(0, pos) + cmd + "[]" + oldText.substring(pos); inputField.setText(newText); inputField.setCaretPosition(pos + cmd.length() + 1); inputField.requestFocus(); } 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(); } /** * action listener implementation for input help panel toggle button */ @Override public void actionPerformed(ActionEvent e) { Object source = e.getSource(); if (source == btnHelpToggle) { // ========================================== // hidden switch to toggle native/crossPlatform LAF if (app.getControlDown() && app.getShiftDown()) { AppD.toggleCrossPlatformLAF(); SwingUtilities.updateComponentTreeUI(app.getFrame()); app.getFrame().pack(); return; } // ========================================= if (btnHelpToggle.isSelected()) { InputBarHelpPanelD helpPanel = (InputBarHelpPanelD) ((GuiManagerD) app .getGuiManager()).getInputHelpPanel(); helpPanel.setLabels(); helpPanel.setCommands(); app.setShowInputHelpPanel(true); } else { app.setShowInputHelpPanel(false); } } } @Override public void keyPressed(KeyEvent e) { // the input field may have consumed this event // for auto completion if (e.isConsumed()) { return; } int keyCode = e.getKeyCode(); switch (keyCode) { case KeyEvent.VK_A: case KeyEvent.VK_C: case KeyEvent.VK_X: case KeyEvent.VK_V: // make sure eg Ctrl-A not passed on return; case KeyEvent.VK_ENTER: onEnterPressed(true); break; default: app.getGlobalKeyDispatcher().handleGeneralKeys(e); // handle eg // ctrl-tab } } private void onEnterPressed(boolean explicit) { if (!explicit && autoInput != null && autoInput.equals(getTextField().getText())) { return; } autoInput = null; app.getKernel().clearJustCreatedGeosInViews(); boolean valid = app.getKernel().getInputPreviewHelper().isValid(); String input = app.getKernel().getInputPreviewHelper() .getInput(getTextField().getText()); if (input == null || input.length() == 0) { app.getActiveEuclidianView().requestFocus(); return; } app.setScrollToShow(true); try { app.getKernel().getAlgebraProcessor() .processAlgebraCommandNoExceptionHandling(input, true, getErrorHandler(valid, explicit), true, new InputBarCallback(app, inputField, input, app.getKernel().getConstructionStep())); } catch (Exception ee) { inputField.addToHistory(getTextField().getText()); app.showGenericError(ee); return; } catch (MyError ee) { inputField.addToHistory(getTextField().getText()); inputField.showError(ee); return; } } /** * Store content of input bar in history (even when it's wrong); update * autoInput to prevent resending */ protected void storeFaultyInput() { inputField.addToHistory(getTextField().getText()); autoInput = getTextField().getText(); } private ErrorHandler getErrorHandler(final boolean valid, final boolean explicit) { return new ErrorHandler() { @Override public void showError(String msg) { if (explicit) { app.getDefaultErrorHandler().showError(msg); storeFaultyInput(); } } @Override public void resetError() { showError(null); } @Override public void showCommandError(String command, String message) { if (explicit) { app.getDefaultErrorHandler().showCommandError(command, message); storeFaultyInput(); } } @Override public String getCurrentCommand() { return inputField.getCommand(); } @Override public boolean onUndefinedVariables(String string, AsyncOperation<String[]> callback) { if (explicit) { if (valid) { return app.getGuiManager() .checkAutoCreateSliders(string, callback); } else if (loc .getReverseCommand(getCurrentCommand()) != null) { ErrorHelper.handleCommandError(loc, getCurrentCommand(), app.getDefaultErrorHandler()); return false; } callback.callback(new String[] { "7" }); } return false; } }; } @Override public void keyReleased(KeyEvent e) { // } @Override public void keyTyped(KeyEvent e) { // } @Override public void focusGained(FocusEvent arg0) { // app.clearSelectedGeos(); } @Override public void focusLost(FocusEvent arg0) { onEnterPressed(false); } @Override public void mouseClicked(MouseEvent e) { // } @Override public void mousePressed(MouseEvent e) { // } @Override public void mouseReleased(MouseEvent e) { // } @Override public void mouseEntered(MouseEvent e) { // make sure tooltips from Tool Bar don't get in the way setToolTipText(""); } @Override public void mouseExited(MouseEvent e) { // } public void setAutoInput(String string) { this.autoInput = string; } }