/*
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.common.main;
import java.util.ArrayList;
import java.util.Arrays;
import org.geogebra.common.awt.GPoint;
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.RedefineInputHandler;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.geos.GeoAngle;
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.GeoPolygon;
import org.geogebra.common.kernel.geos.GeoText;
import org.geogebra.common.kernel.geos.Transformable;
import org.geogebra.common.kernel.kernelND.GeoDirectionND;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.GeoSegmentND;
import org.geogebra.common.main.error.ErrorHandler;
import org.geogebra.common.util.AsyncOperation;
import org.geogebra.common.util.lang.Unicode;
@SuppressWarnings("javadoc")
public abstract class DialogManager {
protected String defaultAngle = Unicode.FORTY_FIVE_DEGREES;
protected App app;
protected Localization loc;
/**
* Dialog for styling text objects.
*/
protected TextInputDialog textInputDialog;
public DialogManager() {
}
public DialogManager(App app) {
this.app = app;
this.loc = app.getLocalization();
}
public abstract boolean showFunctionInspector(GeoFunction geoFunction);
public abstract void showDataSourceDialog(int mode,
boolean doAutoLoadSelectedGeos);
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);
newInputDialog(app, geo.getNameDescription(), loc.getMenu("Redefine"),
str, handler, geo);
}
public abstract InputDialog newInputDialog(App app1, String message,
String title, String initString, InputHandler handler,
GeoElement geo);
public abstract void showNumberInputDialogSegmentFixed(String menu,
GeoPointND geoPoint2);
/**
*
* @param menu
* @param selectedSegments
* @param selectedPoints
* @param selGeos
* @param ec
*/
public void showNumberInputDialogAngleFixed(String menu,
GeoSegmentND[] selectedSegments, GeoPointND[] selectedPoints,
GeoElement[] selGeos, EuclidianController ec) {
doAngleFixed(
app.getKernel(),
selectedSegments,
selectedPoints,
getNumber(app.getKernel(), menu + " " + loc.getMenu("Length"),
""),
false, ec);
}
public static void doAngleFixed(Kernel kernel, GeoSegmentND[] segments,
GeoPointND[] points, GeoNumberValue num,
boolean clockWise, EuclidianController ec) {
// GeoElement circle = kernel.Circle(null, geoPoint1,
// ((NumberInputHandler)inputHandler).getNum());
// geogebra.gui.AngleInputDialog dialog =
// (geogebra.gui.AngleInputDialog) ob[1];
// String angleText = getText();
if (points.length == 2) {
ec.getCompanion().createAngle(points[0], points[1], num,
clockWise);
// (GeoAngle) kernel.getAlgoDispatcher().Angle(null, points[0],
// points[1], num, !clockWise)[0];
} else {
ec.getCompanion().createAngle(segments[0].getEndPoint(),
segments[0].getStartPoint(), num, clockWise);
// (GeoAngle) kernel.getAlgoDispatcher().Angle(null,
// segments[0].getEndPoint(), segments[0].getStartPoint(), num,
// !clockWise)[0];
}
kernel.getApplication().storeUndoInfoAndStateForModeStarting();
}
public boolean showSliderCreationDialog(int x, int y) {
Kernel kernel = app.getKernel();
boolean isAngle = !confirm("OK for number, Cancel for angle");
GeoNumeric slider = GeoNumeric
.setSliderFromDefault(
isAngle ? new GeoAngle(kernel.getConstruction())
: new GeoNumeric(kernel.getConstruction()),
isAngle);
StringTemplate tmpl = StringTemplate.defaultTemplate;
// convert to degrees (angle only)
String minStr = isAngle
? kernel.format(Math.toDegrees(slider.getIntervalMin()), tmpl)
+ Unicode.DEGREE
: kernel.format(slider.getIntervalMin(), tmpl);
String maxStr = isAngle
? kernel.format(Math.toDegrees(slider.getIntervalMax()), tmpl)
+ Unicode.DEGREE
: kernel.format(slider.getIntervalMax(), tmpl);
String incStr = isAngle
? kernel.format(Math.toDegrees(slider.getAnimationStep()), tmpl)
+ Unicode.DEGREE
: kernel.format(slider.getAnimationStep(), tmpl);
// get input from user
NumberValue min = getNumber(kernel, "Enter minimum", minStr);
NumberValue max = getNumber(kernel, "Enter maximum", maxStr);
NumberValue increment = getNumber(kernel, "Enter increment", incStr);
if (min != null) {
slider.setIntervalMin(min);
}
if (max != null) {
slider.setIntervalMax(max);
}
if (increment != null) {
slider.setAnimationStep(increment);
}
slider.setLabel(null);
slider.setValue(isAngle ? 45 * Math.PI / 180 : 1);
slider.setSliderLocation(x, y, true);
slider.setEuclidianVisible(true);
slider.setLabelMode(GeoElement.LABEL_NAME_VALUE);
slider.setLabelVisible(true);
slider.update();
// slider.setRandom(cbRandom.isSelected());
app.storeUndoInfo();
return true;
}
protected abstract boolean confirm(String string);
public void showNumberInputDialogRotate(String menu,
GeoPolygon[] selectedPolygons, GeoPointND[] selectedPoints,
GeoElement[] selGeos, EuclidianController ec) {
String inputString = prompt(menu + " " + loc.getMenu("Angle"),
defaultAngle);
rotateObject(app, inputString, false, selectedPolygons,
new CreateGeoForRotatePoint(selectedPoints[0]), selGeos, ec,
app.getDefaultErrorHandler(), new AsyncOperation<String>() {
@Override
public void callback(String obj) {
defaultAngle = obj;
}
});
}
public abstract void showNumberInputDialogDilate(String menu,
GeoPolygon[] selectedPolygons, GeoPointND[] selectedPoints,
GeoElement[] selGeos, EuclidianController ec);
public abstract void showNumberInputDialogRegularPolygon(String menu,
EuclidianController ec, GeoPointND geoPoint1, GeoPointND geoPoint2);
public abstract void showBooleanCheckboxCreationDialog(GPoint corner,
GeoBoolean bool);
public abstract void showNumberInputDialogCirclePointRadius(String title,
GeoPointND geoPointND, EuclidianView view);
public abstract void showNumberInputDialog(String title, String message,
String initText, AsyncOperation<GeoNumberValue> callback);
public abstract void showNumberInputDialog(String title, String message,
String initText, boolean changingSign, String checkBoxText,
AsyncOperation<GeoNumberValue> callback);
public abstract void showAngleInputDialog(String title, String message,
String initText, AsyncOperation<GeoNumberValue> callback);
public abstract boolean showButtonCreationDialog(int x, int y,
boolean textfield);
public interface CreateGeoForRotate {
public GeoElement[] createGeos(EuclidianController ec, GeoElement geo,
GeoNumberValue num);
public GeoElementND getPivot();
}
public static class CreateGeoForRotatePoint implements CreateGeoForRotate {
private GeoPointND point;
public CreateGeoForRotatePoint(GeoPointND point) {
this.point = point;
}
@Override
public GeoElement[] createGeos(EuclidianController ec, GeoElement geo,
GeoNumberValue num) {
return ec.getCompanion().rotateByAngle(geo, num, point);
}
@Override
public GeoElementND getPivot() {
return point;
}
}
public static class CreateGeoForRotateLine implements CreateGeoForRotate {
private GeoLineND line;
public CreateGeoForRotateLine(GeoLineND line) {
this.line = line;
}
@Override
public GeoElement[] createGeos(EuclidianController ec, GeoElement geo,
GeoNumberValue num) {
return ec.getKernel().getManager3D().Rotate3D(null, geo, num, line);
}
@Override
public GeoElementND getPivot() {
return line;
}
}
public static void rotateObject(final App app, final String angleText,
boolean clockwise, final GeoPolygon[] polys,
final CreateGeoForRotate creator, final GeoElement[] selGeos,
final EuclidianController ec, final ErrorHandler eh,
final AsyncOperation<String> callback) {
String inputText = angleText;
final Kernel kernel = app.getKernel();
// avoid labeling of num
final Construction cons = kernel.getConstruction();
final boolean oldVal = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
// negative orientation ?
if (ec.getCompanion().viewOrientationForClockwise(clockwise)) {
inputText = "-(" + inputText + ")";
}
kernel.getAlgebraProcessor().processAlgebraCommandNoExceptionHandling(
inputText, false, eh, true,
new AsyncOperation<GeoElementND[]>() {
@Override
public void callback(GeoElementND[] result) {
cons.setSuppressLabelCreation(oldVal);
String defaultRotateAngle = Unicode.FORTY_FIVE_DEGREES;
boolean success = result != null && result.length > 0
&& result[0] instanceof GeoNumberValue;
if (success) {
GeoNumberValue num = (GeoNumberValue) result[0];
// keep angle entered if it ends with
// 'degrees'
if (angleText.endsWith(Unicode.DEGREE)) {
defaultRotateAngle = angleText;
}
if (polys.length == 1) {
GeoElement[] geos = creator.createGeos(ec,
polys[0], num);
if (geos != null) {
app.storeUndoInfoAndStateForModeStarting();
ec.memorizeJustCreatedGeos(geos);
kernel.notifyRepaint();
}
if (callback != null) {
callback.callback(defaultRotateAngle);
}
return;
}
ArrayList<GeoElement> ret = new ArrayList<GeoElement>();
for (int i = 0; i < selGeos.length; i++) {
if (selGeos[i] != creator.getPivot()) {
if (selGeos[i] instanceof Transformable) {
ret.addAll(Arrays
.asList(creator.createGeos(ec,
selGeos[i], num)));
} else if (selGeos[i].isGeoPolygon()) {
ret.addAll(Arrays
.asList(creator.createGeos(ec,
selGeos[i], num)));
}
}
}
if (!ret.isEmpty()) {
app.storeUndoInfoAndStateForModeStarting();
ec.memorizeJustCreatedGeos(ret);
kernel.notifyRepaint();
}
} else {
if (result != null && result.length > 0) {
eh.showError(app.getLocalization()
.getError("NumberExpected"));
}
}
if (callback != null) {
callback.callback(
success ? defaultRotateAngle : null);
}
}
});
}
public static void makeRegularPolygon(final App app,
final EuclidianController ec, String inputString,
final GeoPointND geoPoint1, final GeoPointND geoPoint2,
final ErrorHandler handler, final AsyncOperation<Boolean> cb) {
if (inputString == null || "".equals(inputString)) {
if (cb != null) {
cb.callback(false);
}
return;
}
final Kernel kernel = app.getKernel();
final Construction cons = kernel.getConstruction();
// avoid labeling of num
final boolean oldVal = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
AsyncOperation<GeoElementND[]> checkNumber = new AsyncOperation<GeoElementND[]>() {
@Override
public void callback(GeoElementND[] result) {
cons.setSuppressLabelCreation(oldVal);
boolean success = result != null
&& result[0] instanceof GeoNumberValue;
if (!success) {
handler.showError(
app.getLocalization().getError("NumberExpected"));
cb.callback(false);
return;
}
GeoElement[] geos = ec.getCompanion().regularPolygon(geoPoint1,
geoPoint2, (GeoNumberValue) result[0]);
GeoElement[] onlypoly = { null };
if (geos != null) {
onlypoly[0] = geos[0];
app.storeUndoInfoAndStateForModeStarting();
ec.memorizeJustCreatedGeos(onlypoly);
}
if (cb != null) {
cb.callback(success);
}
}
};
kernel.getAlgebraProcessor().processAlgebraCommandNoExceptionHandling(
inputString, false, handler, true, checkNumber);
}
protected GeoNumberValue getNumber(Kernel kernel, String message,
String def) {
Construction cons = kernel.getConstruction();
boolean oldVal = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
String str = prompt(message, def);
GeoNumberValue result = kernel.getAlgebraProcessor()
.evaluateToNumeric(str, true);
cons.setSuppressLabelCreation(oldVal);
return result;
}
protected abstract String prompt(String message, String def);
public abstract void closeAll();
public abstract void showRenameDialog(GeoElement geo, boolean b,
String label, boolean c);
public abstract void showPropertiesDialog(ArrayList<GeoElement> geos);
public abstract void showPropertiesDialog(OptionType type,
ArrayList<GeoElement> geos);
public static boolean doDilate(Kernel kernel, GeoNumberValue num,
GeoPointND[] points, GeoElement[] selGeos, EuclidianController ec) {
if (selGeos.length > 0) {
// mirror all selected geos
// GeoElement [] selGeos = getSelectedGeos();
GeoPointND point = points[0];
ArrayList<GeoElement> ret = new ArrayList<GeoElement>();
for (int i = 0; i < selGeos.length; i++) {
if (selGeos[i] != point) {
if ((selGeos[i] instanceof Transformable)
|| selGeos[i].isGeoList()) {
ret.addAll(Arrays.asList(ec.getCompanion()
.dilateFromPoint(selGeos[i], num, point)));
}
}
}
if (!ret.isEmpty()) {
ec.memorizeJustCreatedGeos(ret);
kernel.getApplication().storeUndoInfoAndStateForModeStarting();
return true;
}
}
return false;
}
public static void doSegmentFixed(Kernel kernel, GeoPointND geoPoint1,
GeoNumberValue num) {
GeoElement[] segment = kernel.getAlgoDispatcher().Segment(null,
geoPoint1, num);
GeoElement[] onlysegment = { null };
if (segment != null) {
onlysegment[0] = segment[0];
kernel.getApplication().storeUndoInfoAndStateForModeStarting();
kernel.getApplication().getActiveEuclidianView()
.getEuclidianController()
.memorizeJustCreatedGeos(onlysegment);
}
}
/**
* Displays the text dialog for a given text.
*/
final public void showTextDialog(GeoText text) {
showTextDialog(text, null, true);
}
/**
* Creates a new text at given startPoint
*
* @param startPoint
* start point position
* @param rw
* true iff in real world coordinates
*/
final public void showTextCreationDialog(GeoPointND startPoint,
boolean rw) {
showTextDialog(null, startPoint, rw);
}
public abstract void openToolHelp();
protected void showTextDialog(GeoText text, GeoPointND startPoint,
boolean rw) {
app.setWaitCursor();
if (textInputDialog == null) {
textInputDialog = createTextDialog(text, startPoint, rw);
} else {
textInputDialog.reInitEditor(text, startPoint, rw);
}
textInputDialog.setVisible(true);
app.setDefaultCursor();
}
public abstract TextInputDialog createTextDialog(GeoText text,
GeoPointND startPoint, boolean rw);
// public abstract void showOpenFromGGTDialog();
/**
* @param ec
* controller
* @param title
* @param geoPoint
*/
public void showNumberInputDialogSpherePointRadius(String title,
GeoPointND geoPoint, EuclidianController ec) {
// 3D stuff
}
/**
* for creating a cone
*
* @param title
* @param a
* basis center
* @param b
* apex point
* @param ec
* controller
*/
public void showNumberInputDialogConeTwoPointsRadius(String title,
GeoPointND a, GeoPointND b, EuclidianController ec) {
// 3D stuff
}
/**
* for creating a cylinder
*
* @param title
* @param a
* basis center
* @param b
* top center
* @param ec
* controller
*/
public void showNumberInputDialogCylinderTwoPointsRadius(String title,
GeoPointND a, GeoPointND b, EuclidianController ec) {
// 3D stuff
}
/**
* @param ec
* controller
* @param title
* @param geoPoint
* @param forAxis
*
*/
public void showNumberInputDialogCirclePointDirectionRadius(String title,
GeoPointND geoPoint, GeoDirectionND forAxis,
EuclidianController ec) {
// 3D stuff
}
/**
* @param title
* @param polys
* @param selectedLines
* @param selGeos
* @param ec
*/
public void showNumberInputDialogRotate(String title, GeoPolygon[] polys,
GeoLineND[] selectedLines, GeoElement[] selGeos,
EuclidianController ec) {
// 3D stuff
}
public boolean hasFunctionInspector() {
return false;
}
public interface CreateGeoFromRadius {
public GeoElement createGeo(Kernel kernel, GeoNumberValue num);
}
public static class CreateSphereFromRadius implements CreateGeoFromRadius {
private GeoPointND point;
public CreateSphereFromRadius(GeoPointND point) {
this.point = point;
}
@Override
public GeoElement createGeo(Kernel kernel, GeoNumberValue num) {
return kernel.getManager3D().Sphere(null, point, num);
}
}
public static class CreateConeFromRadius implements CreateGeoFromRadius {
private GeoPointND point1, point2;
public CreateConeFromRadius(GeoPointND point1, GeoPointND point2) {
this.point1 = point1;
this.point2 = point2;
}
@Override
public GeoElement createGeo(Kernel kernel, GeoNumberValue num) {
return kernel.getManager3D().ConeLimited(null, point1, point2,
num)[0];
}
}
public static class CreateCylinderFromRadius
implements CreateGeoFromRadius {
private GeoPointND point1, point2;
public CreateCylinderFromRadius(GeoPointND point1, GeoPointND point2) {
this.point1 = point1;
this.point2 = point2;
}
@Override
public GeoElement createGeo(Kernel kernel, GeoNumberValue num) {
return kernel.getManager3D().CylinderLimited(null, point1, point2,
num)[0];
}
}
public static class CreateCircleFromDirectionRadius
implements CreateGeoFromRadius {
private GeoPointND point;
private GeoDirectionND forAxis;
public CreateCircleFromDirectionRadius(GeoPointND point,
GeoDirectionND forAxis) {
this.point = point;
this.forAxis = forAxis;
}
@Override
public GeoElement createGeo(Kernel kernel, GeoNumberValue num) {
return kernel.getManager3D().Circle3D(null, point, num, forAxis);
}
}
public static class CreateCircleFromRadius implements CreateGeoFromRadius {
private GeoPointND point;
public CreateCircleFromRadius(GeoPointND point) {
this.point = point;
}
@Override
public GeoElement createGeo(Kernel kernel, GeoNumberValue num) {
return kernel.getAlgoDispatcher().Circle(null, point, num);
}
}
public static void makeGeoPointRadius(final App app,
final EuclidianController ec, String inputString,
final CreateGeoFromRadius createGeoFromRadius,
final ErrorHandler handler,
final AsyncOperation<Boolean> callback) {
if (inputString == null || "".equals(inputString)) {
if (callback != null) {
callback.callback(false);
}
return;
}
final Kernel kernel = app.getKernel();
final Construction cons = kernel.getConstruction();
// avoid labeling of num
final boolean oldVal = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
kernel.getAlgebraProcessor().processAlgebraCommandNoExceptionHandling(
inputString, false, handler, true,
new AsyncOperation<GeoElementND[]>() {
@Override
public void callback(GeoElementND[] result) {
cons.setSuppressLabelCreation(oldVal);
boolean success = result != null
&& result[0] instanceof GeoNumberValue;
if (!success) {
handler.showError(app.getLocalization()
.getError("NumberExpected"));
if (callback != null) {
callback.callback(false);
}
return;
}
GeoElement geo = createGeoFromRadius.createGeo(kernel,
(GeoNumberValue) result[0]);
GeoElement[] onlypoly = { null };
if (geo != null) {
onlypoly[0] = geo;
app.storeUndoInfoAndStateForModeStarting();
ec.memorizeJustCreatedGeos(onlypoly);
kernel.notifyRepaint();
}
if (callback != null) {
callback.callback(geo != null);
}
}
});
}
public static void makeGeoFromNumber(final App app, String inputString,
final AsyncOperation<GeoNumberValue> creator,
final boolean changeSign, final ErrorHandler handler,
final AsyncOperation<Boolean> callback) {
if (inputString == null || "".equals(inputString)) {
if (callback != null) {
callback.callback(false);
}
return;
}
final Kernel kernel = app.getKernel();
final Construction cons = kernel.getConstruction();
// avoid labeling of num
final boolean oldVal = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
// handle change sign
String inputWithSign;
if (changeSign) {
StringBuilder sb = new StringBuilder();
sb.append("-(");
sb.append(inputString);
sb.append(")");
inputWithSign = sb.toString();
} else {
inputWithSign = inputString;
}
kernel.getAlgebraProcessor().processAlgebraCommandNoExceptionHandling(
inputWithSign, false, handler, true,
new AsyncOperation<GeoElementND[]>() {
@Override
public void callback(GeoElementND[] result) {
cons.setSuppressLabelCreation(oldVal);
boolean success = result != null
&& result[0] instanceof GeoNumberValue;
if (!success) {
handler.showError(app.getLocalization()
.getError("NumberExpected"));
if (callback != null) {
callback.callback(false);
}
return;
}
creator.callback((GeoNumberValue) result[0]);
if (callback != null) {
callback.callback(success);
}
}
});
}
}