1 /*******************************************************************************
\r
2 * Copyright (c) 2011 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.charts.editor.e4;
\r
14 import java.awt.Toolkit;
\r
15 import java.awt.datatransfer.Clipboard;
\r
16 import java.awt.datatransfer.StringSelection;
\r
17 import java.io.BufferedWriter;
\r
18 import java.io.File;
\r
19 import java.io.FileOutputStream;
\r
20 import java.io.IOException;
\r
21 import java.io.OutputStreamWriter;
\r
22 import java.lang.reflect.InvocationTargetException;
\r
23 import java.nio.charset.Charset;
\r
24 import java.util.Collections;
\r
25 import java.util.List;
\r
26 import java.util.concurrent.atomic.AtomicBoolean;
\r
28 import javax.inject.Named;
\r
30 import org.eclipse.core.commands.ExecutionException;
\r
31 import org.eclipse.core.runtime.IProgressMonitor;
\r
32 import org.eclipse.core.runtime.preferences.InstanceScope;
\r
33 import org.eclipse.e4.core.di.annotations.CanExecute;
\r
34 import org.eclipse.e4.core.di.annotations.Execute;
\r
35 import org.eclipse.e4.ui.model.application.ui.basic.MPart;
\r
36 import org.eclipse.e4.ui.services.IServiceConstants;
\r
37 import org.eclipse.jface.action.IStatusLineManager;
\r
38 import org.eclipse.jface.operation.IRunnableWithProgress;
\r
39 import org.eclipse.jface.preference.IPreferenceStore;
\r
40 import org.eclipse.swt.SWT;
\r
41 import org.eclipse.swt.widgets.FileDialog;
\r
42 import org.eclipse.swt.widgets.Shell;
\r
43 import org.eclipse.ui.IWorkbenchPart;
\r
44 import org.eclipse.ui.PlatformUI;
\r
45 import org.eclipse.ui.preferences.ScopedPreferenceStore;
\r
46 import org.simantics.charts.ui.CSVProgressMonitor;
\r
47 import org.simantics.databoard.binding.error.BindingException;
\r
48 import org.simantics.databoard.parser.StringEscapeUtils;
\r
49 import org.simantics.databoard.util.Bean;
\r
50 import org.simantics.databoard.util.StreamUtil;
\r
51 import org.simantics.history.HistoryException;
\r
52 import org.simantics.history.ItemManager;
\r
53 import org.simantics.history.csv.CSVFormatter;
\r
54 import org.simantics.history.csv.ColumnSeparator;
\r
55 import org.simantics.history.csv.DecimalSeparator;
\r
56 import org.simantics.history.csv.ExportInterpolation;
\r
57 import org.simantics.history.util.subscription.SamplingFormat;
\r
58 import org.simantics.modeling.preferences.CSVPreferences;
\r
59 import org.simantics.trend.configuration.TrendItem;
\r
60 import org.simantics.trend.impl.TrendNode;
\r
61 import org.simantics.utils.format.FormattingUtils;
\r
62 import org.simantics.utils.ui.ErrorLogger;
\r
63 import org.simantics.utils.ui.workbench.WorkbenchUtils;
\r
66 * @author Tuukka Lehtonen
\r
68 public class ChartCopyHandler {
\r
73 public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part) {
\r
76 if (part.getObject() instanceof TimeSeriesEditor == false)
\r
82 public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, @Named(IServiceConstants.ACTIVE_SHELL) Shell shell) throws ExecutionException {
\r
83 if (part.getObject() instanceof TimeSeriesEditor == false)
\r
85 TimeSeriesEditor editor = (TimeSeriesEditor) part.getObject();
\r
86 final TrendNode trendNode = editor.trendNode;
\r
87 IWorkbenchPart wbpart = WorkbenchUtils.getActiveWorkbenchPart();
\r
88 IStatusLineManager status = WorkbenchUtils.getStatusLine(wbpart);
\r
90 final AtomicBoolean result = new AtomicBoolean(false);
\r
92 PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() {
\r
94 public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
\r
95 result.set( copyDataToClipboard(monitor, trendNode, Format.JOINED_TIME, shell) );
\r
98 if (!result.get()) {
\r
99 status.setMessage("No data to copy");
\r
101 status.setMessage("Copied chart data to clipboard");
\r
103 status.setErrorMessage(null);
\r
104 } catch (InvocationTargetException e) {
\r
105 ErrorLogger.defaultLogError(e.getCause());
\r
106 } catch (InterruptedException e) {
\r
107 ErrorLogger.defaultLogError(e);
\r
111 static enum Format {
\r
116 public boolean copyDataToClipboard(IProgressMonitor monitor, TrendNode t, Format format, final Shell shell) {
\r
117 Charset UTF8 = Charset.forName("UTF-8");
\r
119 // String builder can be really slow when it is extended many times over.
\r
120 // Instead stream to file with buffering
\r
121 // StringBuilder sb = new StringBuilder();
\r
122 IPreferenceStore csvnode = new ScopedPreferenceStore( InstanceScope.INSTANCE, CSVPreferences.P_NODE );
\r
123 String ext = csvnode.getString(CSVPreferences.P_CSV_FILE_EXTENSION);
\r
124 File tmpFile = File.createTempFile("clipboard", ext);
\r
125 tmpFile.deleteOnExit();
\r
126 FileOutputStream fos = new FileOutputStream(tmpFile);
\r
127 BufferedWriter w = new BufferedWriter(new OutputStreamWriter(fos, UTF8));
\r
129 ItemManager im = new ItemManager( t.historian.getItems() );
\r
130 CSVFormatter formatter = new CSVFormatter();
\r
131 formatter.setTimeRange(t.horizRuler.from, t.horizRuler.end);
\r
133 // Write preferences
\r
134 formatter.setStartTime( csvnode.getDouble(CSVPreferences.P_CSV_START_TIME) );
\r
135 formatter.setTimeStep( csvnode.getDouble(CSVPreferences.P_CSV_TIME_STEP) );
\r
136 formatter.setDecimalSeparator( DecimalSeparator.fromPreference(csvnode.getString(CSVPreferences.P_CSV_DECIMAL_SEPARATOR) ) );
\r
137 formatter.setColumnSeparator( ColumnSeparator.fromPreference(StringEscapeUtils.unescape( csvnode.getString(CSVPreferences.P_CSV_COLUMN_SEPARATOR) ) ) );
\r
138 formatter.setResample( csvnode.getBoolean(CSVPreferences.P_CSV_RESAMPLE) );
\r
139 formatter.setNumberInterpolation( ExportInterpolation.fromPreference (csvnode.getString(CSVPreferences.P_CSV_SAMPLING_MODE) ) );
\r
140 formatter.setTimeFormat( FormattingUtils.significantDigitFormat( csvnode.getInt(CSVPreferences.P_CSV_TIME_DIGITS) ) );
\r
141 formatter.setFloatFormat( FormattingUtils.significantDigitFormat( csvnode.getInt(CSVPreferences.P_CSV_FLOAT_DIGITS) ) );
\r
142 formatter.setNumberFormat( FormattingUtils.significantDigitFormat( csvnode.getInt(CSVPreferences.P_CSV_DOUBLE_DIGITS) ) );
\r
144 for (TrendItem i : t.spec.items) {
\r
145 if (i.hidden) continue;
\r
146 List<Bean> items = im.search("variableId", i.variableId);
\r
147 Collections.sort(items, SamplingFormat.INTERVAL_COMPARATOR);
\r
148 if (items.isEmpty()) continue;
\r
149 Bean config = items.get(0);
\r
150 String historyId = (String) config.getFieldUnchecked("id");
\r
151 formatter.addItem( t.historian, historyId, i.simpleLabel, i.variableReference, i.unit);
\r
155 case TIME_VALUE_PAIRS:
\r
156 // formatter.formulate1(new CSVProgressMonitor(monitor), w);
\r
159 formatter.formulate2(new CSVProgressMonitor(monitor), w);
\r
162 throw new UnsupportedOperationException("unsupported format " + format);
\r
166 if (tmpFile.length()==0) return false;
\r
168 Toolkit toolkit = Toolkit.getDefaultToolkit();
\r
169 Clipboard clipboard = toolkit.getSystemClipboard();
\r
174 System.out.println("Exported to "+tmpFile+" size: "+tmpFile.length());
\r
175 if ( tmpFile.length() > 10*1024*1024 ) {
\r
176 // String msg = "The data has been written to temporary file:\n"+tmpFile.getCanonicalPath();
\r
177 // ShowMessage.showInformation( shell.getDisplay(), "Too much data for clipboard.", msg);
\r
178 final File csvFile = tmpFile;
\r
180 shell.getDisplay().asyncExec( new Runnable() {
\r
182 public void run() {
\r
183 FileDialog fd = new FileDialog(shell, SWT.SAVE);
\r
184 fd.setText("Write CSV to File");
\r
185 fd.setFileName( lastFile!=null ? lastFile : csvFile.getAbsolutePath() );
\r
186 String newFile = fd.open();
\r
187 if ( newFile != null ) {
\r
188 lastFile = newFile;
\r
189 File ff = new File( newFile );
\r
191 csvFile.renameTo( ff );
\r
197 String str = StreamUtil.readString(tmpFile, UTF8);
\r
199 StringSelection strSel = new StringSelection(str);
\r
200 clipboard.setContents(strSel, null);
\r
203 } catch (BindingException e1) {
\r
204 ErrorLogger.defaultLogError(e1);
\r
206 } catch (IOException e) {
\r
207 ErrorLogger.defaultLogError(e);
\r
209 if ( fos != null ) try { fos.close(); } catch (IOException e) { ErrorLogger.defaultLogError(e); }
\r
210 if ( tmpFile != null ) tmpFile.delete();
\r
214 } catch (HistoryException e) {
\r
215 ErrorLogger.defaultLogError(e);
\r
216 } catch (IOException e) {
\r
217 ErrorLogger.defaultLogError(e);
\r