/*
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.dialog;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
import org.geogebra.common.gui.InputHandler;
import org.geogebra.common.gui.UpdateFonts;
import org.geogebra.common.gui.VirtualKeyboardListener;
import org.geogebra.common.gui.dialog.InputDialog;
import org.geogebra.common.gui.view.algebra.DialogType;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.main.GeoElementSelectionListener;
import org.geogebra.common.util.AsyncOperation;
import org.geogebra.common.util.StringUtil;
import org.geogebra.common.util.lang.Unicode;
import org.geogebra.desktop.gui.GuiManagerD;
import org.geogebra.desktop.gui.inputfield.AutoCompleteTextFieldD;
import org.geogebra.desktop.gui.util.HelpAction;
import org.geogebra.desktop.gui.view.algebra.InputPanelD;
import org.geogebra.desktop.gui.virtualkeyboard.VirtualKeyboardD;
import org.geogebra.desktop.main.AppD;
import org.geogebra.desktop.main.LocalizationD;
import org.geogebra.desktop.util.GuiResourcesD;
public class InputDialogD extends InputDialog
implements ActionListener, WindowFocusListener, VirtualKeyboardListener,
UpdateFonts, WindowListener {
protected AppD app;
protected final LocalizationD loc;
protected GeoElement geo;
protected GeoElementSelectionListener sl;
public static final int DEFAULT_COLUMNS = 30;
public static final int DEFAULT_ROWS = 10;
protected InputPanelD inputPanel;
protected JPanel optionPane, buttonsPanel, btPanel, btPanel2;
protected JLabel msgLabel;
protected JButton btApply, btCancel, btProperties, btOK, btHelp;
protected JCheckBox checkBox;
protected JDialog wrappedDialog;
private JPanel errorPanel;
/**
* @param app
* @param message
* @param title
* @param initString
* @param autoComplete
* @param handler
*/
public InputDialogD(AppD app, String message, String title,
String initString, boolean autoComplete, InputHandler handler) {
this(app, message, title, initString, autoComplete, handler, false,
false, null);
}
/**
* @param app
* @param message
* @param title
* @param initString
* @param autoComplete
* @param handler
* @param selectInitText
*/
public InputDialogD(AppD app, String message, String title,
String initString, boolean autoComplete, InputHandler handler,
boolean selectInitText) {
this(app, message, title, initString, autoComplete, handler, false,
selectInitText, null);
}
/**
* Creates a non-modal standard input dialog.
*
* @param app
* @param message
* @param title
* @param initString
* @param autoComplete
* @param handler
* @param geo
*/
public InputDialogD(AppD app, String message, String title,
String initString, boolean autoComplete, InputHandler handler,
GeoElement geo) {
this(app, message, title, initString, autoComplete, handler, false,
false, geo);
}
/**
* @param app
* @param message
* @param title
* @param initString
* @param autoComplete
* @param handler
* @param modal
* @param selectInitText
* @param geo
*/
public InputDialogD(AppD app, String message, String title,
String initString, boolean autoComplete, InputHandler handler,
boolean modal, boolean selectInitText, GeoElement geo) {
this(app, message, title, initString, autoComplete, handler, modal,
selectInitText, geo, null, DialogType.GeoGebraEditor);
}
/**
* @param app
* @param message
* @param title
* @param initString
* @param autoComplete
* @param handler
* @param modal
* @param selectInitText
* @param geo
* @param checkBox
* @param type
*/
public InputDialogD(AppD app, String message, String title,
String initString, boolean autoComplete, InputHandler handler,
boolean modal, boolean selectInitText, GeoElement geo,
JCheckBox checkBox, DialogType type) {
this(app.getFrame(), modal, app.getLocalization());
this.app = app;
this.geo = geo;
this.setInputHandler(handler);
this.setInitString(initString);
this.checkBox = checkBox;
// Build the essential GUI: a borderLayout panel with
// a message label on top (North) and a button panel below (South).
// An InputPanel is also created for the center panel, but this is added
// later to allow customizing dialogs.
createGUI(title, message, autoComplete, DEFAULT_COLUMNS, 1, true,
selectInitText, geo != null, geo != null, type);
// wrap inputPanel in a BorderLayout.NORTH component so it keeps a
// single row height when resizing the dialog
JPanel p = new JPanel(new BorderLayout());
p.add(inputPanel, BorderLayout.NORTH);
optionPane.add(p, BorderLayout.CENTER);
// prepare the input panel text selection
if (initString != null && selectInitText) {
inputPanel.selectText();
}
// finalize the GUI
centerOnScreen();
app.setComponentOrientation(wrappedDialog);
wrappedDialog.setResizable(true);
wrappedDialog.pack();
}
/**
* Creates a bare-bones input dialog for highly customized dialogs.
*
* @param frame
* @param modal
*/
@SuppressWarnings("serial")
protected InputDialogD(JFrame frame, boolean modal, LocalizationD loc) {
this.loc = loc;
this.wrappedDialog = new JDialog(frame, modal) {
@Override
public void setVisible(boolean b) {
super.setVisible(b);
if (!b) {
showError(null);
}
handleDialogVisibilityChange(b);
}
};
wrappedDialog.addWindowListener(this);
}
// ===================================================
// GUI
// ===================================================
/**
* @param title
* @param message
* @param autoComplete
* @param columns
* @param rows
* @param showSymbolPopupIcon
* @param selectInitText
* @param showProperties
* @param showApply
* @param type
*/
protected void createGUI(String title, String message, boolean autoComplete,
int columns, int rows, boolean showSymbolPopupIcon,
boolean selectInitText, boolean showProperties, boolean showApply,
DialogType type) {
wrappedDialog.setResizable(true);
// Create components to be displayed
inputPanel = new InputPanelD(getInitString(), app, rows, columns,
showSymbolPopupIcon, type);
sl = new GeoElementSelectionListener() {
@Override
public void geoElementSelected(GeoElement geo1,
boolean addToSelection) {
insertGeoElement(geo1);
inputPanel.getTextComponent().requestFocusInWindow();
}
};
// add listeners to textfield
JTextComponent textComp = inputPanel.getTextComponent();
if (textComp instanceof AutoCompleteTextFieldD) {
AutoCompleteTextFieldD tf = (AutoCompleteTextFieldD) textComp;
tf.setAutoComplete(autoComplete);
tf.addActionListener(this);
}
// create buttons
btProperties = new JButton();
btProperties.setActionCommand("OpenProperties");
btProperties.addActionListener(this);
btOK = new JButton();
btOK.setActionCommand("OK");
btOK.addActionListener(this);
btCancel = new JButton();
btCancel.setActionCommand("Cancel");
btCancel.addActionListener(this);
btApply = new JButton();
btApply.setActionCommand("Apply");
btApply.addActionListener(this);
// create button panels
btPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
btPanel2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
errorPanel = new JPanel(new BorderLayout(5, 5));
buttonsPanel = new JPanel(new BorderLayout(5, 5));
buttonsPanel.add(btPanel2, loc.borderWest()); // used for Help or
buttonsPanel.add(btPanel, loc.borderEast());
// add buttons to panels
loadBtPanel(showApply);
if (showProperties) {
btPanel2.add(btProperties);
}
// =====================================================================
// Create the optionPane: a panel with message label on top, button
// panel on bottom. The center panel holds the inputPanel, which is
// added later.
// =====================================================================
optionPane = new JPanel(new BorderLayout(5, 5));
msgLabel = new JLabel(message);
errorPanel.add(msgLabel, BorderLayout.NORTH);
optionPane.add(errorPanel, BorderLayout.NORTH);
optionPane.add(buttonsPanel, BorderLayout.SOUTH);
optionPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// add optionPane to the dialog
wrappedDialog.setContentPane(optionPane);
setLabels(title);
}
protected void loadBtPanel(boolean showApply) {
btPanel.add(btOK);
btPanel.add(btCancel);
if (showApply) {
btPanel.add(btApply);
}
}
protected void centerOnScreen() {
wrappedDialog.pack();
// center on screen
wrappedDialog.setLocationRelativeTo(app.getMainComponent());
}
public void showSymbolTablePopup(boolean flag) {
inputPanel.showSpecialChars(flag);
}
public void addHelpButton(String articleName) {
btHelp = new JButton();
HelpAction helpAction = new HelpAction(app,
app.getScaledIcon(GuiResourcesD.HELP), loc.getMenu("Help"),
articleName);
btHelp.setAction(helpAction);
btPanel2.add(btHelp);
}
public JPanel getButtonPanel() {
return btPanel;
}
/**
* Update the labels of this component (applied if the language was
* changed).
*
* @param title
* The title of the dialog which is customized for every dialog
*/
public void setLabels(String title) {
wrappedDialog.setTitle(title);
btOK.setText(loc.getMenu("OK"));
btCancel.setText(loc.getMenu("Cancel"));
btApply.setText(loc.getMenu("Apply"));
btProperties.setText(loc.getMenu("Properties") + Unicode.ellipsis);
}
// ===================================================
// Text Handlers
// ===================================================
public void insertGeoElement(GeoElement geo1) {
if (geo1 != null) {
insertString(
" " + geo1.getLabel(StringTemplate.defaultTemplate) + " ");
}
}
@Override
public void insertString(String str) {
insertString(str, false);
}
public void insertString(String str0, boolean isLatex) {
if (str0 != null) {
String str = str0;
if (isLatex) {
// don't convert Greek letters
// * JLaTeXMath can display Unicode
// * it might be inserted in the "box" where Unicode is
// necessary
str = StringUtil.toLaTeXString(str, false);
}
inputPanel.insertString(str);
}
}
public String getText() {
return inputPanel.getText();
}
public void setText(String text) {
inputPanel.setText(text);
}
public void selectText() {
inputPanel.selectText();
}
public void setCaretPosition(int pos) {
JTextComponent tc = inputPanel.getTextComponent();
tc.setCaretPosition(pos);
tc.requestFocusInWindow();
}
public void setRelativeCaretPosition(int pos) {
JTextComponent tc = inputPanel.getTextComponent();
try {
tc.setCaretPosition(tc.getCaretPosition() + pos);
} catch (Exception e) {
// out of bounds
}
tc.requestFocusInWindow();
}
// ===================================================
// Event Handlers
// ===================================================
/**
* Handles button clicks for dialog.
*/
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
// boolean finished = false;
try {
if (source == btOK || source == inputPanel.getTextComponent()) {
inputText = inputPanel.getText();
processInputHandler(new AsyncOperation<Boolean>() {
@Override
public void callback(Boolean ok) {
setVisible(!ok);
}
});
} else if (source == btApply) {
inputText = inputPanel.getText();
processInputHandler(null);
} else if (source == btCancel) {
cancel();
} else if (source == btProperties && geo != null) {
setVisible(false);
openProperties(app, geo);
}
} catch (Exception ex) {
// do nothing on uninitializedValue
setVisible(false);
}
// setVisible(!finished);
}
protected void cancel() {
setVisible(false);
}
// ===================================================
// Visibility Handlers
// ===================================================
public void setVisible(boolean flag) {
if (!wrappedDialog.isModal()) {
if (flag) { // set old mode again
wrappedDialog.addWindowFocusListener(this);
} else {
wrappedDialog.removeWindowFocusListener(this);
app.setSelectionListenerMode(null);
}
}
wrappedDialog.setVisible(flag);
}
public void setVisibleForTools(boolean flag) {
if (!wrappedDialog.isModal()) {
if (flag) { // set old mode again
wrappedDialog.addWindowFocusListener(this);
} else {
wrappedDialog.removeWindowFocusListener(this);
app.setCurrentSelectionListener(null);
}
}
wrappedDialog.setVisible(flag);
}
// ===================================================
// Window Focus Listeners
// ===================================================
@Override
public void windowGainedFocus(WindowEvent arg0) {
if (!wrappedDialog.isModal()) {
app.setSelectionListenerMode(sl);
}
((GuiManagerD) app.getGuiManager()).setCurrentTextfield(this, true);
}
@Override
public void windowLostFocus(WindowEvent arg0) {
((GuiManagerD) app.getGuiManager()).setCurrentTextfield(null,
!(arg0.getOppositeWindow() instanceof VirtualKeyboardD));
}
public JDialog getWrappedDialog() {
return wrappedDialog;
}
@Override
public void updateFonts() {
Font font = app.getPlainFont();
wrappedDialog.setFont(font);
inputPanel.updateFonts();
btOK.setFont(font);
btCancel.setFont(font);
btApply.setFont(font);
btProperties.setFont(font);
}
@Override
public void windowOpened(WindowEvent e) {
// not needed
}
@Override
public void windowClosing(WindowEvent e) {
if (!wrappedDialog.isModal()) {
app.setSelectionListenerMode(null);
}
}
@Override
public void windowClosed(WindowEvent e) {
// this one is actually useful, overriden in subclass(es)
}
@Override
public void windowIconified(WindowEvent e) {
// not needed
}
@Override
public void windowDeiconified(WindowEvent e) {
// not needed
}
@Override
public void windowActivated(WindowEvent e) {
// not needed
}
@Override
public void windowDeactivated(WindowEvent e) {
// not needed
}
public void handleDialogVisibilityChange(boolean isVisible) {
if (!wrappedDialog.isModal() && !isVisible) {
app.setSelectionListenerMode(null);
}
}
private boolean showingError = false;
@Override
public void showError(String msg) {
if (msg == null) {
errorPanel.removeAll();
showingError = false;
errorPanel.add(msgLabel);
} else if (!showingError) {
showingError = true;
errorPanel.removeAll();
JLabel errorLabel = new JLabel(msg);
errorLabel.setForeground(Color.RED);
errorPanel.add(errorLabel);
}
SwingUtilities.updateComponentTreeUI(wrappedDialog);
}
@Override
public void showCommandError(String command, String message) {
app.getDefaultErrorHandler().showCommandError(command, message);
}
@Override
public String getCurrentCommand() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean onUndefinedVariables(String string,
AsyncOperation<String[]> callback) {
return app.getGuiManager().checkAutoCreateSliders(string, callback);
}
@Override
public void resetError() {
showError(null);
}
}