/******************************************************************************* * Copyright (c) 2011 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.charts.editor.e4; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Named; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.e4.core.di.annotations.CanExecute; import org.eclipse.e4.core.di.annotations.Execute; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.services.IServiceConstants; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.preferences.ScopedPreferenceStore; import org.simantics.charts.ui.CSVProgressMonitor; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.parser.StringEscapeUtils; import org.simantics.databoard.util.Bean; import org.simantics.databoard.util.StreamUtil; import org.simantics.history.HistoryException; import org.simantics.history.ItemManager; import org.simantics.history.csv.CSVFormatter; import org.simantics.history.csv.ColumnSeparator; import org.simantics.history.csv.DecimalSeparator; import org.simantics.history.csv.ExportInterpolation; import org.simantics.history.util.subscription.SamplingFormat; import org.simantics.modeling.preferences.CSVPreferences; import org.simantics.trend.configuration.TrendItem; import org.simantics.trend.impl.TrendNode; import org.simantics.utils.format.FormattingUtils; import org.simantics.utils.ui.ErrorLogger; import org.simantics.utils.ui.workbench.WorkbenchUtils; /** * @author Tuukka Lehtonen */ public class ChartCopyHandler { String lastFile; @CanExecute public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part) { if (part == null) return false; if (part.getObject() instanceof TimeSeriesEditor == false) return false; return true; } @Execute public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, @Named(IServiceConstants.ACTIVE_SHELL) Shell shell) throws ExecutionException { if (part.getObject() instanceof TimeSeriesEditor == false) return; TimeSeriesEditor editor = (TimeSeriesEditor) part.getObject(); final TrendNode trendNode = editor.trendNode; IWorkbenchPart wbpart = WorkbenchUtils.getActiveWorkbenchPart(); IStatusLineManager status = WorkbenchUtils.getStatusLine(wbpart); final AtomicBoolean result = new AtomicBoolean(false); try { PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { result.set( copyDataToClipboard(monitor, trendNode, Format.JOINED_TIME, shell) ); } }); if (!result.get()) { status.setMessage("No data to copy"); } else { status.setMessage("Copied chart data to clipboard"); } status.setErrorMessage(null); } catch (InvocationTargetException e) { ErrorLogger.defaultLogError(e.getCause()); } catch (InterruptedException e) { ErrorLogger.defaultLogError(e); } } static enum Format { TIME_VALUE_PAIRS, JOINED_TIME } public boolean copyDataToClipboard(IProgressMonitor monitor, TrendNode t, Format format, final Shell shell) { Charset UTF8 = Charset.forName("UTF-8"); try { // String builder can be really slow when it is extended many times over. // Instead stream to file with buffering // StringBuilder sb = new StringBuilder(); IPreferenceStore csvnode = new ScopedPreferenceStore( InstanceScope.INSTANCE, CSVPreferences.P_NODE ); String ext = csvnode.getString(CSVPreferences.P_CSV_FILE_EXTENSION); File tmpFile = File.createTempFile("clipboard", ext); tmpFile.deleteOnExit(); FileOutputStream fos = new FileOutputStream(tmpFile); BufferedWriter w = new BufferedWriter(new OutputStreamWriter(fos, UTF8)); try { ItemManager im = new ItemManager( t.historian.getItems() ); CSVFormatter formatter = new CSVFormatter(); formatter.setTimeRange(t.horizRuler.from, t.horizRuler.end); // Write preferences formatter.setStartTime( csvnode.getDouble(CSVPreferences.P_CSV_START_TIME) ); formatter.setTimeStep( csvnode.getDouble(CSVPreferences.P_CSV_TIME_STEP) ); formatter.setDecimalSeparator( DecimalSeparator.fromPreference(csvnode.getString(CSVPreferences.P_CSV_DECIMAL_SEPARATOR) ) ); formatter.setColumnSeparator( ColumnSeparator.fromPreference(StringEscapeUtils.unescape( csvnode.getString(CSVPreferences.P_CSV_COLUMN_SEPARATOR) ) ) ); formatter.setResample( csvnode.getBoolean(CSVPreferences.P_CSV_RESAMPLE) ); formatter.setNumberInterpolation( ExportInterpolation.fromPreference (csvnode.getString(CSVPreferences.P_CSV_SAMPLING_MODE) ) ); formatter.setTimeFormat( FormattingUtils.significantDigitFormat( csvnode.getInt(CSVPreferences.P_CSV_TIME_DIGITS) ) ); formatter.setFloatFormat( FormattingUtils.significantDigitFormat( csvnode.getInt(CSVPreferences.P_CSV_FLOAT_DIGITS) ) ); formatter.setNumberFormat( FormattingUtils.significantDigitFormat( csvnode.getInt(CSVPreferences.P_CSV_DOUBLE_DIGITS) ) ); for (TrendItem i : t.spec.items) { if (i.hidden) continue; List items = im.search("variableId", i.variableId); Collections.sort(items, SamplingFormat.INTERVAL_COMPARATOR); if (items.isEmpty()) continue; Bean config = items.get(0); String historyId = (String) config.getFieldUnchecked("id"); formatter.addItem( t.historian, historyId, i.simpleLabel, i.variableReference, i.unit); } formatter.sort(); switch (format) { case TIME_VALUE_PAIRS: // formatter.formulate1(new CSVProgressMonitor(monitor), w); break; case JOINED_TIME: formatter.formulate2(new CSVProgressMonitor(monitor), w); break; default: throw new UnsupportedOperationException("unsupported format " + format); } w.flush(); if (tmpFile.length()==0) return false; Toolkit toolkit = Toolkit.getDefaultToolkit(); Clipboard clipboard = toolkit.getSystemClipboard(); w.flush(); fos.close(); fos = null; System.out.println("Exported to "+tmpFile+" size: "+tmpFile.length()); if ( tmpFile.length() > 10*1024*1024 ) { // String msg = "The data has been written to temporary file:\n"+tmpFile.getCanonicalPath(); // ShowMessage.showInformation( shell.getDisplay(), "Too much data for clipboard.", msg); final File csvFile = tmpFile; tmpFile = null; shell.getDisplay().asyncExec( new Runnable() { @Override public void run() { FileDialog fd = new FileDialog(shell, SWT.SAVE); fd.setText("Write CSV to File"); fd.setFileName( lastFile!=null ? lastFile : csvFile.getAbsolutePath() ); String newFile = fd.open(); if ( newFile != null ) { lastFile = newFile; File ff = new File( newFile ); ff.delete(); csvFile.renameTo( ff ); } else { csvFile.delete(); } }} ); } else { String str = StreamUtil.readString(tmpFile, UTF8); StringSelection strSel = new StringSelection(str); clipboard.setContents(strSel, null); } } catch (BindingException e1) { ErrorLogger.defaultLogError(e1); return false; } catch (IOException e) { ErrorLogger.defaultLogError(e); } finally { if ( fos != null ) try { fos.close(); } catch (IOException e) { ErrorLogger.defaultLogError(e); } if ( tmpFile != null ) tmpFile.delete(); } return true; } catch (HistoryException e) { ErrorLogger.defaultLogError(e); } catch (IOException e) { ErrorLogger.defaultLogError(e); } return false; } }