package org.geogebra.web.web.gui.view.spreadsheet;
import org.geogebra.common.gui.view.spreadsheet.CopyPasteCut;
import org.geogebra.common.gui.view.spreadsheet.DataImport;
import org.geogebra.common.gui.view.spreadsheet.RelativeCopy;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.main.App;
import org.geogebra.web.html5.Browser;
import org.geogebra.web.html5.main.AppW;
public class CopyPasteCutW extends CopyPasteCut {
private static String staticClipboardString = "";
public CopyPasteCutW(App app) {
super(app);
}
/**
* Just copying the selection as string text format, independently!
*/
public String copyString(int column1, int row1, int column2, int row2) {
StringBuilder cellBufferStrLoc = new StringBuilder();
for (int row = row1; row <= row2; ++row) {
for (int column = column1; column <= column2; ++column) {
GeoElement value = RelativeCopy.getValue(app, column, row);
if (value != null) {
String valueString = value
.toValueString(StringTemplate.maxPrecision);
// for aesthetical copying, it is also good to remove
// trailing zeroes (zero is nothing anyway):
int indx = valueString.indexOf(app.getKernel().getLocalization().unicodeDecimalPoint);
if (indx > -1) {
int end = valueString.length() - 1;
// only in this case, we should remove trailing zeroes!
while (valueString.charAt(end) == '0') {
end--;
}
if (end == indx) {
end--;
}
valueString = valueString.substring(0, end + 1);
}
cellBufferStrLoc.append(valueString);
}
if (column != column2) {
cellBufferStrLoc.append('\t');
}
}
if (row != row2) {
cellBufferStrLoc.append('\n');
}
}
return new String(cellBufferStrLoc);
}
@Override
public void copy(int column1, int row1, int column2, int row2,
boolean skipGeoCopy) {
copy(column1, row1, column2, row2, skipGeoCopy, false);
}
public static native boolean checkClipboardSupported() /*-{
if ($doc.queryCommandSupported("copy")) {
return true;
}
return false;
}-*/;
public void copy(int column1, int row1, int column2, int row2,
boolean skipGeoCopy, boolean nat) {
sourceColumn1 = column1;
sourceRow1 = row1;
// copy tab-delimited geo values into the external buffer
if (getCellBufferStr() == null) {
setCellBufferStr(new StringBuilder());
} else {
getCellBufferStr().setLength(0);
}
for (int row = row1; row <= row2; ++row) {
for (int column = column1; column <= column2; ++column) {
GeoElement geo = RelativeCopy.getValue(app, column, row);
if (geo != null) {
getCellBufferStr().append(geo.toValueString(StringTemplate.defaultTemplate));
}
if (column != column2) {
getCellBufferStr().append('\t');
}
}
if (row != row2) {
getCellBufferStr().append('\n');
}
}
// store the tab-delimited values in the clipboard
/*Toolkit toolkit = Toolkit.getDefaultToolkit();
Clipboard clipboard = toolkit.getSystemClipboard();
StringSelection stringSelection = new StringSelection(cellBufferStr);
clipboard.setContents(stringSelection, null);*/
// a clipboard inside this application is better than nothing
//staticClipboardString = new String(cellBufferStr);
if (nat) {
// if called from native event, setting clipboard contents
// is not crucial, and redundant/harmful in IE...
setInternalClipboardContents(new String(getCellBufferStr()));
} else {
((AppW) app).copyTextToSystemClipboard(
new String(getCellBufferStr()),
getFocusCallback());
}
// store copies of the actual geos in the internal buffer
if (skipGeoCopy) {
setCellBufferGeo(null);
} else {
setCellBufferGeo(RelativeCopy.getValues(app, column1, row1, column2,
row2));
}
}
private Runnable getFocusCallback() {
return new Runnable() {
@Override
public void run() {
getTable().editCellAt(sourceColumn1, sourceRow1);
}
};
}
@Override
/** Paste data from the clipboard */
public boolean paste(int column1, int row1, int column2, int row2) {
/*Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable contents = clipboard.getContents(null);*/
String contents = getClipboardContents(new Runnable() {
@Override
public void run() {
getTable().editCellAt(sourceColumn1, sourceRow1); // reset focus
}
});
return paste( column1, row1, column2, row2, contents);
}
/**
* Pastes data from given Transferable into the given spreadsheet cells.
*
* @param column1
* first column of the target cell range
* @param row1
* first row of the target cell range
* @param column2
* last column of the target cell range
* @param row2
* last row of the target cell range
* @param contents
* string to paste into cells
* @return
* true if pasting was successful
*/
public boolean paste(int column1, int row1, int column2, int row2,
String contents) {
boolean succ = false;
//boolean isCSV = false;
String transferString = null;
// extract a String from the Transferable contents
transferString = contents;
if (transferString == null) {
return false;
}
// isCSV = DataImport.hasHTMLFlavor(contents);
// App.debug("transfer string: " + transferString);
// test if the transfer string is the same as the internal cell copy
// string. If true, then we have a tab-delimited list of cell geos and
// can paste them with relative cell references
boolean doInternalPaste = getCellBufferStr() != null
&& transferString.equals(getCellBufferStr().toString());
if (doInternalPaste && getCellBufferGeo() != null) {
// use the internal field cellBufferGeo to paste geo copies
// with relative cell references
succ = pasteInternalMultiple(column1, row1, column2, row2);
} else {
// use the transferString data to create and paste new geos
// into the target cells without relative cell references
boolean isCSV = false;
String[][] data = DataImport.parseExternalData(app, transferString, isCSV);
succ = pasteExternalMultiple(data, column1, row1, column2, row2);
/* old hack
// in theory
// special case: hacking in Web, input is coming from us
String[] data0 = transferString.split("\n");
String[] data00 = data0[0].split("\t");
String[][] data = new String[data0.length][data00.length];
for (int i = 0; i < data0.length; i++)
data[i] = data0[i].split("\t");
// String[][] data = DataImportW.parseExternalData(app,
// transferString, null,
// isCSV);
succ = pasteExternalMultiple(data, column1, row1, column2, row2);*/
// Application.debug("newline index "+buf.indexOf("\n"));
// Application.debug("length "+buf.length());
}
return succ;
}
/**
* Just for the copy, removing redundancy runtime
*/
public boolean cut(int column1, int row1, int column2, int row2, boolean nat) {
copy(column1, row1, column2, row2, false, nat);
// null out the external buffer so that paste will not do a relative
// copy
setCellBufferStr(null);
return delete(column1, row1, column2, row2);
}
/**
* When using the default functionality of the browser,
* getting/setting clipboard contents is solved quite well,
* and it uses the external clipboard. Thus this method is
* redundant in case it's called from paste event! For the
* same reason, it is not called from there.
*
* However, we may call the same thing from GeoGebraWeb
* context menu, and in that case the form of this method
* is just Okay.
*
* @return String
*/
public static String getClipboardContents(Runnable onFocusChange) {
String clipboard = null;
if (isChromeWebapp()) { // use chrome web app paste API
clipboard = getSystemClipboardChromeWebapp();
if (onFocusChange != null) {
onFocusChange.run();
}
} else if (Browser.isInternetExplorer()) {
clipboard = getSystemClipboardIE();
} else { // use internal clipboard
clipboard = staticClipboardString;
}
return clipboard;
}
/**
* As copying to system clipboard is supposed to have done
* @param value String
*/
private static void setInternalClipboardContents(String value) {
staticClipboardString = value;
}
private static native boolean isChromeWebapp() /*-{
// check if the app is running in chrome and is installed (has an id)
// the function is defined in app.html
return $doc.isChromeWebapp();
}-*/;
private static native String getSystemClipboardChromeWebapp() /*-{
var copyFrom = @org.geogebra.web.html5.main.AppW::getHiddenTextArea()();
copyFrom.select();
$doc.execCommand('paste');
var contents = copyFrom.value;
return contents;
}-*/;
private static native String getSystemClipboardIE() /*-{
return $wnd.clipboardData.getData('Text');
}-*/;
public static native void copyToSystemClipboardIE(String value) /*-{
return $wnd.clipboardData.setData('Text', value);
}-*/;
}