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