package org.geogebra.desktop.gui.dialog;
import java.awt.Point;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.geogebra.common.awt.GPoint;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.euclidian.EuclidianController;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.gui.InputHandler;
import org.geogebra.common.gui.dialog.InputDialog;
import org.geogebra.common.gui.dialog.TextInputDialog;
import org.geogebra.common.gui.dialog.handler.NumberChangeSignInputHandler;
import org.geogebra.common.gui.dialog.handler.NumberInputHandler;
import org.geogebra.common.gui.dialog.handler.RedefineInputHandler;
import org.geogebra.common.gui.dialog.handler.RenameInputHandler;
import org.geogebra.common.gui.view.properties.PropertiesView;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.geos.GProperty;
import org.geogebra.common.kernel.geos.GeoBoolean;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoNumberValue;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoPolygon;
import org.geogebra.common.kernel.geos.GeoText;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.GeoSegmentND;
import org.geogebra.common.main.App;
import org.geogebra.common.main.OptionType;
import org.geogebra.common.util.AsyncOperation;
import org.geogebra.common.util.debug.Log;
import org.geogebra.desktop.gui.GuiManagerD;
import org.geogebra.desktop.gui.app.MyFileFilter;
import org.geogebra.desktop.gui.autocompletion.AutoCompletion;
import org.geogebra.desktop.gui.toolbar.ToolbarConfigDialog;
import org.geogebra.desktop.gui.util.GeoGebraFileChooser;
import org.geogebra.desktop.gui.view.data.DataSourceDialog;
import org.geogebra.desktop.gui.view.functioninspector.FunctionInspectorD;
import org.geogebra.desktop.main.AppD;
import org.geogebra.desktop.main.DialogManagerMinimal;
import org.geogebra.desktop.main.LocalizationD;
import org.geogebra.desktop.main.MyResourceBundle;
/**
* Class to manage all kind of dialogs, including the file chooser, appearing in
* GeoGebra. Supports (explicit) lazy initialization so that dialogs have to be
* created manually if needed.
*/
public class DialogManagerD extends DialogManagerMinimal {
/**
* Dialog to view properties of a function.
*/
private FunctionInspectorD functionInspector;
/**
* Dialog to select new files, either for loading or saving. Various file
* types are supported.
*/
private GeoGebraFileChooser fileChooser;
private DataSourceDialog dataSourceDialog;
/**
* Properties for translation of file chooser UI in languages Java doesn't
* support.
*/
private ResourceBundle rbJavaUI;
/**
* Keep track of the current locale for file chooser UI updating.
*/
private Locale currentLocale;
public DialogManagerD(App app) {
super(app);
}
/**
* Update the fonts used in the dialogs.
*/
public void updateFonts() {
if (functionInspector != null) {
functionInspector.updateFonts();
}
if (textInputDialog != null) {
((TextInputDialogD) textInputDialog).updateFonts();
}
if (fileChooser != null) {
fileChooser.setFont(((AppD) app).getPlainFont());
SwingUtilities.updateComponentTreeUI(fileChooser);
}
if (dataSourceDialog != null) {
dataSourceDialog.updateFonts(((AppD) app).getPlainFont());
}
}
/**
* Update labels in the GUI.
*/
public void setLabels() {
if (functionInspector != null) {
functionInspector.setLabels();
}
if (textInputDialog != null) {
((TextInputDialogD) textInputDialog).setLabels();
}
if (fileChooser != null) {
updateJavaUILanguage();
}
if (dataSourceDialog != null) {
dataSourceDialog.setLabels();
}
}
/**
* Open properties view
*/
public void showPropertiesDialog() {
showPropertiesDialog(null);
}
@Override
public void showPropertiesDialog(ArrayList<GeoElement> geos) {
showPropertiesDialog(OptionType.OBJECTS, geos);
}
/**
* Displays the properties dialog
*/
@Override
public void showPropertiesDialog(OptionType type,
ArrayList<GeoElement> geos) {
if (!((AppD) app).letShowPropertiesDialog()) {
return;
}
// get PropertiesView
PropertiesView pv = (PropertiesView) ((GuiManagerD) app.getGuiManager())
.getPropertiesView();
// select geos
if (geos != null) {
if (app.getSelectionManager().getSelectedGeos().size() == 0) {
app.getSelectionManager().addSelectedGeos(geos, true);
}
}
// set properties option type
if (type != null) {
pv.setOptionPanel(type);
}
// show the view
((GuiManagerD) app.getGuiManager()).setShowView(true,
App.VIEW_PROPERTIES);
if (geos != null && geos.size() == 1 && geos.get(0).isEuclidianVisible()
&& geos.get(0) instanceof GeoNumeric) {
// AbstractApplication.debug("TODO : propPanel.showSliderTab()");
((GuiManagerD) app.getGuiManager()).showPropertiesViewSliderTab();
}
}
/**
* Displays the configuration dialog for the toolbar
*/
@Override
public void showToolbarConfigDialog() {
app.getActiveEuclidianView().resetMode();
ToolbarConfigDialog dialog = new ToolbarConfigDialog(((AppD) app));
dialog.setVisible(true);
}
/**
* Displays the rename dialog for geo
*/
@Override
public void showRenameDialog(GeoElement geo, boolean storeUndo,
String initText, boolean selectInitText) {
if (!app.isRightClickEnabled()) {
return;
}
geo.setLabelVisible(true);
geo.updateVisualStyleRepaint(GProperty.LABEL_STYLE);
if (app.getGuiManager() != null) {
app.getGuiManager().clearInputbar();
}
InputHandler handler = new RenameInputHandler(app, geo, storeUndo);
// a Chinese friendly version
InputDialogD id = new InputDialogD(((AppD) app),
"<html>" + app.getLocalization().getPlain("NewNameForA",
"<b>" + geo.getNameDescription() + "</b>") + // eg New
// name
// for
// <b>Segment
// a</b>
"</html>",
loc.getMenu("Rename"), initText, false, handler, false,
selectInitText, null);
/*
* InputDialog id = new InputDialog( this, "<html>" +
* loc.getMenu("NewName") + " " + loc.getMenu("for") + " <b>" +
* geo.getNameDescription() + "</b></html>", loc.getMenu("Rename"),
* initText, false, handler, true, selectInitText);
*/
id.setVisible(true);
}
/**
* Displays the redefine dialog for geo
*
* @param allowTextDialog
* whether text dialog should be used for texts
*/
@Override
public void showRedefineDialog(GeoElement geo, boolean allowTextDialog) {
if (allowTextDialog && geo.isGeoText() && !geo.isTextCommand()) {
showTextDialog((GeoText) geo);
return;
}
String str = geo.getRedefineString(false, true);
InputHandler handler = new RedefineInputHandler(app, geo, str);
InputDialogD id = new InputDialogD(((AppD) app),
geo.getNameDescription(), loc.getMenu("Redefine"), str, true,
handler, geo);
id.showSymbolTablePopup(true);
id.setVisible(true);
}
/**
* Shows the function inspector dialog. If none exists, a new inspector is
* created.
*/
@Override
public boolean showFunctionInspector(GeoFunction function) {
boolean success = true;
try {
if (functionInspector == null) {
functionInspector = new FunctionInspectorD(((AppD) app),
function);
} else {
functionInspector.insertGeoElement(function);
}
functionInspector.show();
} catch (Exception e) {
success = false;
e.printStackTrace();
}
return success;
}
/**
* Creates a new data source dialog
*/
@Override
public void showDataSourceDialog(int mode, boolean doAutoLoadSelectedGeos) {
if (dataSourceDialog == null) {
dataSourceDialog = new DataSourceDialog(((AppD) app), mode);
} else {
dataSourceDialog.updateDialog(mode, doAutoLoadSelectedGeos);
}
dataSourceDialog.setVisible(true);
}
/**
* Creates a new checkbox at given startPoint
*/
@Override
public void showBooleanCheckboxCreationDialog(GPoint loc, GeoBoolean bool) {
Point location = new Point(loc.x, loc.y);
CheckboxCreationDialog d = new CheckboxCreationDialog(((AppD) app),
location, bool);
d.setVisible(true);
}
/**
* Shows a modal dialog to enter a number or number variable name.
*/
@Override
public void showNumberInputDialog(String title, String message,
String initText, AsyncOperation<GeoNumberValue> callback) {
// avoid labeling of num
final Construction cons = app.getKernel().getConstruction();
boolean oldVal = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
NumberInputHandler handler = new NumberInputHandler(
app.getKernel().getAlgebraProcessor(), callback, app, oldVal);
InputDialogD id = new InputDialogD(((AppD) app), message, title,
initText, false, handler, true, false, null) {
@Override
protected void cancel() {
cons.setSuppressLabelCreation(false);
super.cancel();
}
};
id.setVisible(true);
}
/**
* Shows a modal dialog to enter a number or number variable name.
*/
@Override
public void showNumberInputDialog(String title, String message,
String initText, boolean changingSign, String checkBoxText,
AsyncOperation<GeoNumberValue> callback) {
// avoid labeling of num
Construction cons = app.getKernel().getConstruction();
boolean oldVal = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
NumberChangeSignInputHandler handler = new NumberChangeSignInputHandler(
app.getKernel().getAlgebraProcessor(), callback, app, oldVal);
NumberChangeSignInputDialog id = new NumberChangeSignInputDialog(
((AppD) app), message, title, initText, handler, changingSign,
checkBoxText);
id.setVisible(true);
cons.setSuppressLabelCreation(oldVal);
}
@Override
public void showNumberInputDialogRegularPolygon(String title,
EuclidianController ec, GeoPointND geoPoint1,
GeoPointND geoPoint2) {
NumberInputHandler handler = new NumberInputHandler(
app.getKernel().getAlgebraProcessor());
InputDialogD id = new InputDialogRegularPolygonD(((AppD) app), ec,
title, handler, geoPoint1, geoPoint2);
id.setVisible(true);
}
@Override
public void showNumberInputDialogCirclePointRadius(String title,
GeoPointND geoPoint1, EuclidianView view) {
NumberInputHandler handler = new NumberInputHandler(
app.getKernel().getAlgebraProcessor());
InputDialogD id = new InputDialogCirclePointRadius(((AppD) app), title,
handler, (GeoPoint) geoPoint1, app.getKernel());
id.setVisible(true);
}
@Override
public void showNumberInputDialogRotate(String title, GeoPolygon[] polys,
GeoPointND[] points, GeoElement[] selGeos, EuclidianController ec) {
NumberInputHandler handler = new NumberInputHandler(
app.getKernel().getAlgebraProcessor());
InputDialogD id = new InputDialogRotatePointD(((AppD) app), title,
handler, polys, points, selGeos, ec);
id.setVisible(true);
}
@Override
public void showNumberInputDialogAngleFixed(String title,
GeoSegmentND[] segments, GeoPointND[] points, GeoElement[] selGeos,
EuclidianController ec) {
NumberInputHandler handler = new NumberInputHandler(
app.getKernel().getAlgebraProcessor());
InputDialogD id = new InputDialogAngleFixedD(((AppD) app), title,
handler, segments, points, app.getKernel(), ec);
id.setVisible(true);
}
@Override
public void showNumberInputDialogDilate(String title, GeoPolygon[] polys,
GeoPointND[] points, GeoElement[] selGeos, EuclidianController ec) {
NumberInputHandler handler = new NumberInputHandler(
app.getKernel().getAlgebraProcessor());
InputDialogD id = new InputDialogDilateD(((AppD) app), title, handler,
points, selGeos, app.getKernel(), ec);
id.setVisible(true);
}
@Override
public void showNumberInputDialogSegmentFixed(String title,
GeoPointND geoPoint1) {
NumberInputHandler handler = new NumberInputHandler(
app.getKernel().getAlgebraProcessor());
InputDialogD id = new InputDialogSegmentFixedD(((AppD) app), title,
handler, geoPoint1, app.getKernel());
id.setVisible(true);
}
/**
* Shows a modal dialog to enter an angle or angle variable name.
*
* @return: Object[] with { NumberValue, AngleInputDialog } pair
*/
@Override
public void showAngleInputDialog(String title, String message,
String initText, AsyncOperation<GeoNumberValue> callback) {
// avoid labeling of num
Construction cons = app.getKernel().getConstruction();
boolean oldVal = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
NumberInputHandler handler = new NumberInputHandler(
app.getKernel().getAlgebraProcessor(), callback, app, oldVal);
AngleInputDialogD id = new AngleInputDialogD(((AppD) app), message, title,
initText, false, handler, true);
id.setVisible(true);
}
/**
* Close all open dialogs.
*
*/
@Override
public void closeAll() {
// closePropertiesDialog();
if (functionInspector != null) {
functionInspector.hide();
}
}
/**
* Creates a new slider at given location (screen coords).
*
* @return whether a new slider (number) was create or not
*/
@Override
public boolean showSliderCreationDialog(int x, int y) {
app.setWaitCursor();
SliderDialogD dialog = new SliderDialogD(((AppD) app), x, y);
dialog.setVisible(true);
app.setDefaultCursor();
return true;
}
@Override
public void showLogInDialog() {
app.setWaitCursor();
SignInDialogD dialog = new SignInDialogD((AppD) app);
app.isShowingLogInDialog();
dialog.setVisible(true);
app.setDefaultCursor();
}
@Override
public void showLogOutDialog() {
Object[] options = { loc.getMenu("SignOut"), loc.getMenu("Cancel") };
int n = JOptionPane.showOptionDialog(((AppD) app).getMainComponent(),
loc.getMenu("ReallySignOut"), loc.getMenu("Question"),
JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[0]);
if (n == 0) {
app.getLoginOperation().performLogOut();
}
}
/**
* Opens dialog for Tube
*/
public void showOpenFromGGTDialog() {
app.setWaitCursor();
WebViewDialog dialog = new OpenFromGGTDialogD((AppD) app);
dialog.setVisible(true);
app.setDefaultCursor();
}
/**
* Creates a new JavaScript button at given location (screen coords).
*
* @return whether a new slider (number) was create or not
*/
@Override
public boolean showButtonCreationDialog(int x, int y, boolean textfield) {
ButtonDialogD dialog = new ButtonDialogD(((AppD) app), x, y, textfield);
dialog.setVisible(true);
return true;
}
public synchronized void initFileChooser() {
if (fileChooser == null) {
try {
setFileChooser(new GeoGebraFileChooser(((AppD) app),
((AppD) app).getCurrentImagePath())); // non-restricted
fileChooser.addPropertyChangeListener(
JFileChooser.FILE_FILTER_CHANGED_PROPERTY,
new FileFilterChangedListener());
} catch (Exception e) {
// fix for java.io.IOException: Could not get shell folder ID
// list
// Java bug
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6544857
Log.debug(
"Error creating GeoGebraFileChooser - using fallback option");
setFileChooser(new GeoGebraFileChooser(((AppD) app),
((AppD) app).getCurrentImagePath(), true)); // restricted
// version
}
updateJavaUILanguage();
}
}
/**
* Loads java-ui.properties and sets all key-value pairs using
* UIManager.put(). This is needed to translate JFileChooser to languages
* not supported by Java natively.
*/
private void updateJavaUILanguage() {
// load properties jar file
if (currentLocale == ((AppD) app).getLocale()) {
return;
}
// update locale
currentLocale = ((AppD) app).getLocale();
String lang = currentLocale.getLanguage();
boolean deleteKeys = false;
if ("it".equals(lang) || "zh".equals(lang) || "ja".equals(lang)
|| "de".equals(lang)
// || "es".equals(lang) we have our own Spanish translation
// || "fr".equals(lang) we have our own French translation
|| "ko".equals(lang) || "sv".equals(lang)) {
// get keys to delete
// as Java is localized in these languages already
// http://openjdk.java.net/groups/i18n/
rbJavaUI = MyResourceBundle
.loadSingleBundleFile(LocalizationD.RB_JAVA_UI);
deleteKeys = true;
} else {
rbJavaUI = MyResourceBundle.createBundle(LocalizationD.RB_JAVA_UI,
currentLocale);
}
Enumeration<String> keys = rbJavaUI.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
String value = deleteKeys ? null : rbJavaUI.getString(key);
UIManager.put(key, value);
}
// update file chooser
if (getFileChooser() != null) {
getFileChooser().setLocale(currentLocale);
SwingUtilities.updateComponentTreeUI(getFileChooser());
// Unfortunately the preceding line removes the event listener from
// the
// internal JTextField inside the file chooser. This means that the
// listener has to be registered again. (e.g. a simple call to
// 'AutoCompletion.install(this);' inside the GeoGebraFileChooser
// constructor is not sufficient)
AutoCompletion.install(getFileChooser(), true);
}
}
public GeoGebraFileChooser getFileChooser() {
return fileChooser;
}
public void setFileChooser(GeoGebraFileChooser fileChooser) {
this.fileChooser = fileChooser;
}
public FunctionInspectorD getFunctionInspector() {
return functionInspector;
}
public TextInputDialog getTextInputDialog() {
return textInputDialog;
}
public DataSourceDialog getDataSourceDialog() {
return dataSourceDialog;
}
/*
* PropertyChangeListener implementation to handle file filter changes
*/
private class FileFilterChangedListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (getFileChooser().getFileFilter() instanceof MyFileFilter) {
String fileName = null;
if (getFileChooser().getSelectedFile() != null) {
fileName = getFileChooser().getSelectedFile().getName();
} else {
fileName = ((GuiManagerD) app.getGuiManager())
.getLastFileNameOfSaveDialog();
}
// fileName = getFileName(fileName);
if (fileName != null && fileName.indexOf(".") > -1) {
fileName = fileName.substring(0, fileName.lastIndexOf("."))
+ "."
+ ((MyFileFilter) getFileChooser().getFileFilter())
.getExtension();
getFileChooser().setSelectedFile(new File(
getFileChooser().getCurrentDirectory(), fileName));
}
}
}
}
/**
* Factory for the {@link DialogManagerD} class.
*/
public static class Factory {
/**
* @param app
* Application instance
* @return a new {@link DialogManagerD}
*/
public DialogManagerD create(AppD app) {
DialogManagerD dialogManager = new DialogManagerD(app);
return dialogManager;
}
}
@Override
protected String prompt(String message, String def) {
Log.debug("Shouldn't ever be called");
return null;
}
@Override
protected boolean confirm(String string) {
Log.debug("Shouldn't ever be called");
return false;
}
@Override
public void openToolHelp() {
new HelpDialog((AppD) app).openToolHelp();
}
@Override
public TextInputDialog createTextDialog(GeoText text, GeoPointND startPoint,
boolean rw) {
return new TextInputDialogD(app, loc.getMenu("Text"), text, startPoint,
rw, 30, 6, app.getMode() == EuclidianConstants.MODE_TEXT);
}
@Override
public InputDialog newInputDialog(App app, String message, String title,
String initString, InputHandler handler, GeoElement geo) {
return new InputDialogD((AppD) app, message, title, initString,
true, handler, geo);
}
}