/*
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.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.euclidian.EuclidianViewInterfaceCommon;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Construction.Constants;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.Macro;
import org.geogebra.common.kernel.algos.AlgoCirclePointRadius;
import org.geogebra.common.kernel.algos.AlgoCircleThreePoints;
import org.geogebra.common.kernel.algos.AlgoCircleTwoPoints;
import org.geogebra.common.kernel.algos.AlgoConicFivePoints;
import org.geogebra.common.kernel.algos.AlgoDependentList;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoEllipseHyperbolaFociPoint;
import org.geogebra.common.kernel.algos.AlgoInputBox;
import org.geogebra.common.kernel.algos.AlgoJoinPoints;
import org.geogebra.common.kernel.algos.AlgoJoinPointsRay;
import org.geogebra.common.kernel.algos.AlgoJoinPointsSegment;
import org.geogebra.common.kernel.algos.AlgoMacro;
import org.geogebra.common.kernel.algos.AlgoPolyLine;
import org.geogebra.common.kernel.algos.AlgoPolygon;
import org.geogebra.common.kernel.algos.AlgoPolygonRegularND;
import org.geogebra.common.kernel.algos.AlgoVector;
import org.geogebra.common.kernel.algos.Algos;
import org.geogebra.common.kernel.algos.ConstructionElement;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPolyLine;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.main.App;
import org.geogebra.common.util.debug.Log;
/**
* This class saves the given geos (which are usually the selected ones) into an
* XML string, and makes it possible to insert a copy of them into the
* construction As a nature of the clipboard, this class contains only static
* data and methods
*
* @author Arpad Fekete
*/
public class CopyPaste {
// labelPrefix has to contain something else than big letters,
// otherwise the parsed label could be regarded as a spreadsheet label
// see GeoElement.isSpreadsheetLabel
// check if name is valid for geo
public static final String labelPrefix = "CLIPBOARDmagicSTRING";
protected HashSet<Macro> copiedMacros;
protected StringBuilder copiedXML;
protected ArrayList<String> copiedXMLlabels;
protected StringBuilder copiedXMLforSameWindow;
protected ArrayList<String> copiedXMLlabelsforSameWindow;
protected EuclidianViewInterfaceCommon copySource;
protected Object copyObject, copyObject2;
/**
* Returns whether the clipboard is empty
*
* @return whether the clipboard is empty
*/
public boolean isEmpty() {
if (copiedXML == null) {
return true;
}
return (copiedXML.length() == 0);
}
/**
* copyToXML - Step 1 Remove fixed sliders
*
* @param geos
* input and output
*/
protected void removeFixedSliders(ArrayList<ConstructionElement> geos) {
GeoElement geo;
for (int i = geos.size() - 1; i >= 0; i--) {
geo = (GeoElement) geos.get(i);
if (geo.isGeoNumeric()) {
if (((GeoNumeric) geo).isSliderFixed()) {
geos.remove(geo);
}
}
}
}
/**
* copyToXML - Step 1.5 (temporary) currently we remove all geos which
* depend on the axes TODO: make geos dependent on GeoAxis objects copiable
* again (this is not easy as there is a bug when copy & paste something
* which depends on xAxis or yAxis, then copy & paste something else, points
* may be repositioned)
*
*/
protected void removeDependentFromAxes(ArrayList<ConstructionElement> geos,
App app) {
TreeSet<GeoElement> ancestors = new TreeSet<GeoElement>();
ConstructionElement geo;
for (int i = geos.size() - 1; i >= 0; i--) {
geo = geos.get(i);
ancestors.clear();
geo.addPredecessorsToSet(ancestors, true);
if (ancestors.contains(app.getKernel().getXAxis())) {
geos.remove(i);
} else if (ancestors.contains(app.getKernel().getYAxis())) {
geos.remove(i);
} else if (app.is3D()) {
Construction cons = app.getKernel().getConstruction();
if (ancestors.contains(app.getKernel().getXAxis3D())) {
geos.remove(i);
} else if (ancestors.contains(app.getKernel().getYAxis3D())) {
geos.remove(i);
} else if (ancestors.contains(app.getKernel().getZAxis3D())) {
geos.remove(i);
} else if (ancestors.contains(cons.getXOYPlane())) {
geos.remove(i);
} else if (ancestors
.contains(cons.getClippingCube())) {
geos.remove(i);
} else if (ancestors.contains(cons.getSpace())) {
geos.remove(i);
}
}
}
}
protected void removeHavingMacroPredecessors(
ArrayList<ConstructionElement> geos, boolean copymacro) {
GeoElement geo, geo2;
Iterator<GeoElement> it;
boolean found = false;
for (int i = geos.size() - 1; i >= 0; i--) {
if (geos.get(i).isGeoElement()) {
geo = (GeoElement) geos.get(i);
found = false;
if (geo.getParentAlgorithm() != null) {
if (geo.getParentAlgorithm().getClassName()
.equals(Algos.AlgoMacro)) {
found = true;
if (copymacro) {
copiedMacros
.add(((AlgoMacro) geo.getParentAlgorithm())
.getMacro());
}
}
}
if (!found) {
it = geo.getAllPredecessors().iterator();
while (it.hasNext()) {
geo2 = it.next();
if (geo2.getParentAlgorithm() != null) {
if (geo2.getParentAlgorithm().getClassName()
.equals(Algos.AlgoMacro)) {
found = true;
if (copymacro) {
copiedMacros.add(((AlgoMacro) geo2
.getParentAlgorithm()).getMacro());
}
break;
}
}
}
}
if (found && !copymacro) {
geos.remove(i);
}
}
}
}
/**
* copyToXML - Step 2 Add subgeos of geos like points of a segment or line
* or polygon These are copied anyway but this way they won't be hidden
*
* @param geos
* input and output
*/
protected void addSubGeos(ArrayList<ConstructionElement> geos) {
GeoElement geo;
for (int i = geos.size() - 1; i >= 0; i--) {
geo = (GeoElement) geos.get(i);
if (geo.getParentAlgorithm() == null) {
continue;
}
if (!geo.isGeoElement3D()) {
if ((geo.isGeoLine()
&& geo.getParentAlgorithm() instanceof AlgoJoinPoints)
|| (geo.isGeoSegment()
&& geo.getParentAlgorithm() instanceof AlgoJoinPointsSegment)
|| (geo.isGeoRay()
&& geo.getParentAlgorithm() instanceof AlgoJoinPointsRay)
|| (geo.isGeoVector() && geo
.getParentAlgorithm() instanceof AlgoVector)) {
if (!geos
.contains(geo.getParentAlgorithm().getInput()[0])) {
geos.add(geo.getParentAlgorithm().getInput()[0]);
}
if (!geos
.contains(geo.getParentAlgorithm().getInput()[1])) {
geos.add(geo.getParentAlgorithm().getInput()[1]);
}
} else if (geo.isGeoPolygon()) {
if (geo.getParentAlgorithm() instanceof AlgoPolygon) {
GeoPointND[] points = ((AlgoPolygon) (geo
.getParentAlgorithm())).getPoints();
for (int j = 0; j < points.length; j++) {
if (!geos.contains(points[j])) {
geos.add((GeoElement) points[j]);
}
}
GeoElement[] ogeos = ((AlgoPolygon) (geo
.getParentAlgorithm())).getOutput();
for (int j = 0; j < ogeos.length; j++) {
if (!geos.contains(ogeos[j])
&& ogeos[j].isGeoSegment()) {
geos.add(ogeos[j]);
}
}
} else if (geo
.getParentAlgorithm() instanceof AlgoPolygonRegularND) {
GeoElement[] pgeos = ((geo.getParentAlgorithm()))
.getInput();
for (int j = 0; j < pgeos.length; j++) {
if (!geos.contains(pgeos[j])
&& pgeos[j].isGeoPoint()) {
geos.add(pgeos[j]);
}
}
GeoElement[] ogeos = ((geo.getParentAlgorithm()))
.getOutput();
for (int j = 0; j < ogeos.length; j++) {
if (!geos.contains(ogeos[j])
&& (ogeos[j].isGeoSegment()
|| ogeos[j].isGeoPoint())) {
geos.add(ogeos[j]);
}
}
}
} else if (geo instanceof GeoPolyLine) {
if (geo.getParentAlgorithm() instanceof AlgoPolyLine) {
GeoPointND[] pgeos = ((AlgoPolyLine) (geo
.getParentAlgorithm())).getPoints();
for (int j = 0; j < pgeos.length; j++) {
if (!geos.contains(pgeos[j])) {
geos.add((GeoElement) pgeos[j]);
}
}
}
} else if (geo.isGeoConic()) {
if (geo.getParentAlgorithm() instanceof AlgoCircleTwoPoints) {
GeoElement[] pgeos = geo.getParentAlgorithm()
.getInput();
if (!geos.contains(pgeos[0])) {
geos.add(pgeos[0]);
}
if (!geos.contains(pgeos[1])) {
geos.add(pgeos[1]);
}
} else if (geo
.getParentAlgorithm() instanceof AlgoCircleThreePoints
|| geo.getParentAlgorithm() instanceof AlgoEllipseHyperbolaFociPoint) {
GeoElement[] pgeos = geo.getParentAlgorithm()
.getInput();
if (!geos.contains(pgeos[0])) {
geos.add(pgeos[0]);
}
if (!geos.contains(pgeos[1])) {
geos.add(pgeos[1]);
}
if (!geos.contains(pgeos[2])) {
geos.add(pgeos[2]);
}
} else if (geo
.getParentAlgorithm() instanceof AlgoConicFivePoints) {
GeoElement[] pgeos = geo.getParentAlgorithm()
.getInput();
for (int j = 0; j < pgeos.length; j++) {
if (!geos.contains(pgeos[j])) {
geos.add(pgeos[j]);
}
}
} else if (geo
.getParentAlgorithm() instanceof AlgoCirclePointRadius) {
GeoElement[] pgeos = geo.getParentAlgorithm()
.getInput();
if (!geos.contains(pgeos[0])) {
geos.add(pgeos[0]);
}
}
} else if (geo.isGeoList()) {
// TODO: note that there are a whole lot of other list algos
// that might need to be supported! 3D cases come here too,
// because GeoList is 2D object! Or we should make a
// separate
// method just for GeoList! It would also be good, for
// nested
// lists in lists and GeoElements with subGeos in lists!
// (new ticket)
if (geo.getParentAlgorithm().getClassName()
.equals(Commands.Sequence)) {
GeoElement[] pgeos = geo.getParentAlgorithm()
.getInput();
if (pgeos.length > 1) {
if (!geos.contains(pgeos[0])) {
geos.add(pgeos[0]);
}
}
} else if (geo
.getParentAlgorithm() instanceof AlgoDependentList) {
GeoElement[] pgeos = geo.getParentAlgorithm()
.getInput();
for (int j = 0; j < pgeos.length; j++) {
if (!geos.contains(pgeos[j])) {
geos.add(pgeos[j]);
}
}
}
}
}
}
}
/**
* copyToXML - Step 3 Add geos which might be intermediates between our
* existent geos And also add all predecessors of our geos except GeoAxis
* objects (GeoAxis objects should be dealt with later - we suppose they are
* always on)
*
* @param geos
* input and output
* @return just the predecessor and intermediate geos for future handling
*/
protected ArrayList<ConstructionElement> addPredecessorGeos(
ArrayList<ConstructionElement> geos) {
ArrayList<ConstructionElement> ret = new ArrayList<ConstructionElement>();
GeoElement geo, geo2;
TreeSet<GeoElement> ts;
Iterator<GeoElement> it;
for (int i = 0; i < geos.size(); i++) {
geo = (GeoElement) geos.get(i);
ts = geo.getAllPredecessors();
it = ts.iterator();
while (it.hasNext()) {
geo2 = it.next();
if (!ret.contains(geo2) && !geos.contains(geo2)
&& geo2.getConstruction()
.isConstantElement(geo2) == Constants.NOT) {
ret.add(geo2);
}
}
}
geos.addAll(ret);
return ret;
}
/**
* copyToXML - Step 4 Add the algos which belong to our selected geos Also
* add the geos which might be side-effects of these algos
*
* @param conels
* input and output
* @return the possible side-effect geos
*/
protected ArrayList<ConstructionElement> addAlgosDependentFromInside(
ArrayList<ConstructionElement> conels, boolean putdown,
boolean copymacro) {
ArrayList<ConstructionElement> ret = new ArrayList<ConstructionElement>();
GeoElement geo;
ArrayList<AlgoElement> geoal;
AlgoElement ale;
ArrayList<ConstructionElement> ac;
GeoElement[] geos;
for (int i = conels.size() - 1; i >= 0; i--) {
geo = (GeoElement) conels.get(i);
// also doing this here, which is not about the name of the method,
// but making sure textfields (which require algos) are shown
if ((geo.getParentAlgorithm() instanceof AlgoInputBox)
&& (!ret.contains(geo.getParentAlgorithm()))
&& (!conels.contains(geo.getParentAlgorithm()))) {
// other algos will be added to this anyway,
// so we can handle this issue in this method
ret.add(geo.getParentAlgorithm());
}
// probably not needed? although corner number is NumberValue,
// it is converted to a GeoElement, so this might be Okay
/*
* if ((geo.getParentAlgorithm() instanceof AlgoDrawingPadCorner) &&
* (!ret.contains(geo.getParentAlgorithm())) &&
* (!geos.contains(geo.getParentAlgorithm()))) { // other algos will
* be added to this anyway, // so we can handle this issue in this
* method ret.add(geo.getParentAlgorithm()); }
*/
geoal = geo.getAlgorithmList();
for (int j = 0; j < geoal.size(); j++) {
ale = geoal.get(j);
if (!(ale instanceof AlgoMacro) || putdown || copymacro) {
ac = new ArrayList<ConstructionElement>();
ac.addAll(Arrays.asList(ale.getInput()));
if (conels.containsAll(ac) && !conels.contains(ale)) {
if ((ale instanceof AlgoMacro) && copymacro) {
copiedMacros.add(((AlgoMacro) ale).getMacro());
}
conels.add(ale);
geos = ale.getOutput();
if (geos != null) {
for (int k = 0; k < geos.length; k++) {
if (!ret.contains(geos[k])
&& !conels.contains(geos[k])) {
ret.add(geos[k]);
}
}
}
}
}
}
}
conels.addAll(ret);
return ret;
}
/**
* copyToXML - Step 4.5 If copied to the same window, don't copy free
* non-selected GeoNumerics
*
* @param conels
* @param selected
*/
protected ArrayList<ConstructionElement> removeFreeNonselectedGeoNumerics(
ArrayList<ConstructionElement> conels,
ArrayList<GeoElement> selected) {
ArrayList<ConstructionElement> ret = new ArrayList<ConstructionElement>();
ret.addAll(conels);
GeoElement geo;
for (int i = ret.size() - 1; i >= 0; i--) {
if (ret.get(i).isGeoElement()) {
geo = (GeoElement) ret.get(i);
if (geo.isGeoNumeric() && geo.isIndependent()
&& !selected.contains(geo)) {
ret.remove(i);
}
}
}
return ret;
}
/**
* copyToXML - Step 5 Before saving the conels to xml, we have to rename its
* labels with labelPrefix and memorize those renamed labels and also hide
* the GeoElements in geostohide, and keep in geostohide only those which
* were actually hidden...
*
* @param conels
* @param geostohide
*/
protected void beforeSavingToXML(ArrayList<ConstructionElement> conels,
ArrayList<ConstructionElement> geostohide, boolean samewindow,
boolean putdown) {
if (samewindow) {
copiedXMLlabelsforSameWindow = new ArrayList<String>();
} else {
copiedXMLlabels = new ArrayList<String>();
}
ConstructionElement geo;
String label;
for (int i = 0; i < conels.size(); i++) {
geo = conels.get(i);
if (geo.isGeoElement()) {
label = ((GeoElement) geo).getLabelSimple();
if (label != null) {
((GeoElement) geo).setLabelSimple(labelPrefix + label);
if (samewindow) {
copiedXMLlabelsforSameWindow
.add(((GeoElement) geo).getLabelSimple());
} else {
copiedXMLlabels
.add(((GeoElement) geo).getLabelSimple());
}
if (putdown) {
geo.getKernel().renameLabelInScripts(label,
labelPrefix + label);
}
// TODO: check possible realLabel issues
// reallabel = ((GeoElement)geo).getRealLabel();
// if (!reallabel.equals( ((GeoElement)geo).getLabelSimple()
// )) {
// ((GeoElement)geo).setRealLabel(labelPrefix + reallabel);
// }
}
}
}
for (int j = geostohide.size() - 1; j >= 0; j--) {
geo = geostohide.get(j);
if (geo.isGeoElement() && ((GeoElement) geo).isEuclidianVisible()) {
((GeoElement) geo).setEuclidianVisible(false);
} else {
geostohide.remove(geo);
}
}
}
/**
* copyToXML - Step 6 After saving the conels to xml, we have to rename its
* labels and also show the GeoElements in geostoshow
*
* @param conels
* @param geostoshow
*/
protected void afterSavingToXML(ArrayList<ConstructionElement> conels,
ArrayList<ConstructionElement> geostoshow, boolean putdown) {
ConstructionElement geo;
String label;
for (int i = 0; i < conels.size(); i++) {
geo = conels.get(i);
if (geo.isGeoElement()) {
label = ((GeoElement) geo).getLabelSimple();
if (label != null && label.length() >= labelPrefix.length()) {
if (label.substring(0, labelPrefix.length())
.equals(labelPrefix)) {
try {
((GeoElement) geo).setLabelSimple(
label.substring(labelPrefix.length()));
if (putdown) {
geo.getKernel().renameLabelInScripts(label,
label.substring(labelPrefix.length()));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
for (int j = geostoshow.size() - 1; j >= 0; j--) {
geo = geostoshow.get(j);
if (geo.isGeoElement()) {
((GeoElement) geo).setEuclidianVisible(true);
}
}
}
/**
* This method saves geos and all predecessors of them in XML
*
* @param app
* the App object (the main application instance)
* @param geos
* the set of GeoElement's that should be copied
* @param putdown
* boolean which means the InsertFile case
*/
public void copyToXML(App app, ArrayList<GeoElement> geos,
boolean putdown) {
boolean copyMacrosPresume = true;
if (geos.isEmpty()) {
return;
}
boolean scriptsBlocked = app.isBlockUpdateScripts();
app.setBlockUpdateScripts(true);
copiedXML = new StringBuilder();
copiedXMLlabels = new ArrayList<String>();
copiedXMLforSameWindow = new StringBuilder();
copiedXMLlabelsforSameWindow = new ArrayList<String>();
copySource = app.getActiveEuclidianView();
copyObject = app.getKernel().getConstruction().getUndoManager()
.getCurrentUndoInfo();
copiedMacros = new HashSet<Macro>();
// create geoslocal and geostohide
ArrayList<ConstructionElement> geoslocal = new ArrayList<ConstructionElement>();
geoslocal.addAll(geos);
if (!putdown) {
removeFixedSliders(geoslocal);
}
if (geoslocal.isEmpty()) {
app.setBlockUpdateScripts(scriptsBlocked);
return;
}
removeDependentFromAxes(geoslocal, app);
if (geoslocal.isEmpty()) {
app.setBlockUpdateScripts(scriptsBlocked);
return;
}
if (!putdown) {
removeHavingMacroPredecessors(geoslocal, copyMacrosPresume);
if (geoslocal.isEmpty()) {
app.setBlockUpdateScripts(scriptsBlocked);
return;
}
}
addSubGeos(geoslocal);
if (geoslocal.isEmpty()) {
app.setBlockUpdateScripts(scriptsBlocked);
return;
}
ArrayList<ConstructionElement> geostohide = addPredecessorGeos(
geoslocal);
// what about a GeoElement which is the result of an algo with no input?
// this is especially important if the GeoElement cannot be shown,
// e.g. in case the GeoElement is a textfield. In other cases, the bug
// is
// not visible, but it would still be nice to include the parent algo
// too.
// it is okay to handle it after this, as algos are resistant to hiding
geostohide.addAll(addAlgosDependentFromInside(geoslocal, putdown,
copyMacrosPresume));
ArrayList<ConstructionElement> geoslocalsw = removeFreeNonselectedGeoNumerics(
geoslocal, geos);
ArrayList<ConstructionElement> geostohidesw = removeFreeNonselectedGeoNumerics(
geostohide, geos);
Kernel kernel = app.getKernel();
// // FIRST XML SAVE
beforeSavingToXML(geoslocal, geostohide, false, putdown);
// change kernel settings temporarily
// StringType oldPrintForm = kernel.getStringTemplate().getStringType();
boolean saveScriptsToXML = kernel.getSaveScriptsToXML();
if (!putdown) {
kernel.setSaveScriptsToXML(false);
}
try {
// step 5
copiedXML = new StringBuilder();
ConstructionElement ce;
// loop through Construction to keep the good order of
// ConstructionElements
Construction cons = app.getKernel().getConstruction();
for (int i = 0; i < cons.steps(); ++i) {
ce = cons.getConstructionElement(i);
if (geoslocal.contains(ce)) {
ce.getXML(false, copiedXML);
}
}
} catch (Exception e) {
e.printStackTrace();
copiedXML = new StringBuilder();
}
// restore kernel settings
// kernel.setCASPrintForm(oldPrintForm);
if (!putdown) {
kernel.setSaveScriptsToXML(saveScriptsToXML);
}
afterSavingToXML(geoslocal, geostohide, putdown);
// FIRST XML SAVE END
// SECOND XML SAVE
if (!putdown) {
beforeSavingToXML(geoslocalsw, geostohidesw, true, putdown);
kernel.setSaveScriptsToXML(false);
try {
// step 5
copiedXMLforSameWindow = new StringBuilder();
ConstructionElement ce;
// loop through Construction to keep the good order of
// ConstructionElements
Construction cons = app.getKernel().getConstruction();
for (int i = 0; i < cons.steps(); ++i) {
ce = cons.getConstructionElement(i);
if (geoslocalsw.contains(ce)) {
ce.getXML(false, copiedXMLforSameWindow);
}
}
} catch (Exception e) {
e.printStackTrace();
copiedXMLforSameWindow = new StringBuilder();
}
// restore kernel settings
// kernel.setCASPrintForm(oldPrintForm);
kernel.setSaveScriptsToXML(saveScriptsToXML);
afterSavingToXML(geoslocalsw, geostohidesw, putdown);
}
// SECOND XML SAVE END
app.setMode(EuclidianConstants.MODE_MOVE);
app.getActiveEuclidianView().setSelectionRectangle(null);
app.setBlockUpdateScripts(scriptsBlocked);
}
/**
* In some situations, we may need to clear the clipboard
*/
public void clearClipboard() {
copiedXML = null;
copiedXMLlabels = new ArrayList<String>();
copiedXMLforSameWindow = null;
copiedXMLlabelsforSameWindow = new ArrayList<String>();
copySource = null;
copyObject = null;
copyObject2 = null;
copiedMacros = null;
}
/**
* Convenience method to set new labels instead of labels
*
* @param app
* @param labels
*/
protected void handleLabels(App app, ArrayList<String> labels,
boolean putdown) {
Kernel kernel = app.getKernel();
GeoElement geo;
String oldLabel;
for (int i = 0; i < labels.size(); i++) {
String ll = labels.get(i);
geo = kernel.lookupLabel(ll);
if (geo != null) {
if (app.getActiveEuclidianView() == app.getEuclidianView1()) {
app.addToEuclidianView(geo);
if (app.hasEuclidianView2(1)) {
geo.removeView(App.VIEW_EUCLIDIAN2);
app.getEuclidianView2(1).remove(geo);
}
if (app.isEuclidianView3Dinited()) {
app.removeFromViews3D(geo);
}
} else if (app.getActiveEuclidianView()
.getViewID() == App.VIEW_EUCLIDIAN3D) {
app.removeFromEuclidianView(geo);
if (app.isEuclidianView3Dinited()) {
app.addToViews3D(geo);
}
if (app.hasEuclidianView2(1)) {
geo.removeView(App.VIEW_EUCLIDIAN2);
app.getEuclidianView2(1).remove(geo);
}
} else {
app.removeFromEuclidianView(geo);
geo.addView(App.VIEW_EUCLIDIAN2);
app.getEuclidianView2(1).add(geo);
if (app.isEuclidianView3Dinited()) {
app.removeFromViews3D(geo);
}
}
oldLabel = geo.getLabelSimple();
geo.setLabel(geo.getIndexLabel(
geo.getLabelSimple().substring(labelPrefix.length())));
// geo.getLabelSimple() is now not the oldLabel, ideally
if (putdown) {
geo.getKernel().renameLabelInScripts(oldLabel,
geo.getLabelSimple());
}
// geo.setLabel(geo.getDefaultLabel(false));
app.getSelectionManager().addSelectedGeo(geo);
if (geo.getParentAlgorithm() != null) {
if (geo.getParentAlgorithm().getClassName()
.equals(Commands.Sequence)) {
// variable of AlgoSequence is not returned in
// lookupLabel!
// the old name of the variable may remain, as it is not
// part of the construction anyway
GeoElement[] pgeos = geo.getParentAlgorithm()
.getInput();
if (pgeos.length > 1 && pgeos[1].getLabelSimple()
.length() > labelPrefix.length()) {
if (pgeos[1].getLabelSimple()
.substring(0, labelPrefix.length())
.equals(labelPrefix)) {
pgeos[1].setLabelSimple(
pgeos[1].getLabelSimple().substring(
labelPrefix.length()));
}
}
}
}
}
}
}
/**
* Checks whether the copyXMLforSameWindow may be used
*
* @param app
* @return boolean
*/
public boolean pasteFast(App app) {
if (app.getActiveEuclidianView() != copySource) {
return false;
}
if (copyObject != copyObject2) {
return false;
}
return true;
}
/**
* This method pastes the content of the clipboard from XML into the
* construction
*
* @param app
*/
public void pasteFromXML(App app, boolean putdown) {
if (copiedXML == null) {
return;
}
if (copiedXML.length() == 0) {
return;
}
if (!app.getActiveEuclidianView().getEuclidianController().mayPaste()) {
return;
}
copyObject2 = app.getKernel().getConstruction().getUndoManager()
.getCurrentUndoInfo();
if (pasteFast(app) && !putdown) {
if (copiedXMLforSameWindow == null) {
return;
}
if (copiedXMLforSameWindow.length() == 0) {
return;
}
}
if(pasteFast(app)){
app.getKernel().notifyPaste(copiedXMLforSameWindow.toString());
} else {
app.getKernel().notifyPaste(copiedXML.toString());
}
// it turned out to be necessary for e.g. handleLabels
boolean scriptsBlocked = app.isBlockUpdateScripts();
app.setBlockUpdateScripts(true);
// don't update selection
app.getActiveEuclidianView().getEuclidianController()
.clearSelections(true, false);
// don't update properties view
app.updateSelection(false);
app.getActiveEuclidianView().getEuclidianController()
.setPastePreviewSelected();
if (pasteFast(app) && !putdown) {
EuclidianViewInterfaceCommon ev = app.getActiveEuclidianView();
if (ev == app.getEuclidianView1()) {
app.getGgbApi().evalXML(copiedXMLforSameWindow.toString());
app.getKernel().getConstruction().updateConstruction();
app.setActiveView(App.VIEW_EUCLIDIAN);
} else if (ev == app.getEuclidianView3D()) {
app.getGgbApi().evalXML(copiedXMLforSameWindow.toString());
app.getKernel().getConstruction().updateConstruction();
app.setActiveView(App.VIEW_EUCLIDIAN3D);
} else {
app.getGgbApi().evalXML(copiedXMLforSameWindow.toString());
app.getKernel().getConstruction().updateConstruction();
app.setActiveView(App.VIEW_EUCLIDIAN2);
}
handleLabels(app, copiedXMLlabelsforSameWindow, putdown);
} else {
// here the possible macros should be copied as well,
// in case we should copy any macros
if (!copiedMacros.isEmpty()) {
// now we have to copy the macros from ad to app
// in order to make some advanced constructions work
// as it was hard to copy macro classes, let's use
// strings, but how to load them into the application?
try {
// app.getXMLio().processXMLString(copySource.getApplication().getMacroXML(),
// false, true);
// alternative solution
app.addMacroXML(copySource.getApplication().getXMLio()
.getFullMacroXML(
new ArrayList<Macro>(copiedMacros)));
} catch (Exception ex) {
Log.debug(
"Could not load any macros at \"Paste from XML\"");
ex.printStackTrace();
}
}
EuclidianViewInterfaceCommon ev = app.getActiveEuclidianView();
if (ev == app.getEuclidianView1()) {
app.getGgbApi().evalXML(copiedXML.toString());
app.getKernel().getConstruction().updateConstruction();
app.setActiveView(App.VIEW_EUCLIDIAN);
} else if (ev == app.getEuclidianView3D()) {
app.getGgbApi().evalXML(copiedXML.toString());
app.getKernel().getConstruction().updateConstruction();
app.setActiveView(App.VIEW_EUCLIDIAN3D);
} else {
app.getGgbApi().evalXML(copiedXML.toString());
app.getKernel().getConstruction().updateConstruction();
app.setActiveView(App.VIEW_EUCLIDIAN2);
}
handleLabels(app, copiedXMLlabels, putdown);
}
app.setBlockUpdateScripts(scriptsBlocked);
if (!putdown) {
app.getActiveEuclidianView().getEuclidianController()
.setPastePreviewSelected();
}
app.setMode(EuclidianConstants.MODE_MOVE);
app.getKernel().notifyPasteComplete();
}
/**
* Currently, we call this only if the pasted object is put down, but it
* would be better if this were called every time when kernel.storeUndoInfo
* called and there wasn't anything deleted
*/
public void pastePutDownCallback(App app) {
if (pasteFast(app)) {
copyObject = app.getKernel().getConstruction().getUndoManager()
.getCurrentUndoInfo();
copyObject2 = null;
}
}
}