Fixed StandardCopyHandler SWT threading regression
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / modelBrowser / handlers / StandardCopyHandler.java
1 /*******************************************************************************
2  * Copyright (c) 2007 VTT Technical Research Centre of Finland and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     VTT Technical Research Centre of Finland - initial API and implementation
10  *******************************************************************************/
11 package org.simantics.modeling.ui.modelBrowser.handlers;
12
13 import gnu.trove.set.hash.THashSet;
14
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Set;
18
19 import org.eclipse.core.commands.AbstractHandler;
20 import org.eclipse.core.commands.ExecutionEvent;
21 import org.eclipse.core.commands.ExecutionException;
22 import org.eclipse.core.runtime.IProgressMonitor;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.SubMonitor;
25 import org.eclipse.core.runtime.jobs.Job;
26 import org.eclipse.jface.action.IStatusLineManager;
27 import org.eclipse.jface.viewers.ISelection;
28 import org.eclipse.jface.viewers.StructuredSelection;
29 import org.eclipse.swt.dnd.Clipboard;
30 import org.eclipse.swt.dnd.TextTransfer;
31 import org.eclipse.swt.dnd.Transfer;
32 import org.eclipse.swt.widgets.Control;
33 import org.eclipse.swt.widgets.Display;
34 import org.eclipse.swt.widgets.Shell;
35 import org.eclipse.swt.widgets.Tree;
36 import org.eclipse.swt.widgets.TreeItem;
37 import org.eclipse.ui.PlatformUI;
38 import org.eclipse.ui.handlers.HandlerUtil;
39 import org.simantics.Simantics;
40 import org.simantics.browsing.ui.BuiltinKeys;
41 import org.simantics.browsing.ui.GraphExplorer;
42 import org.simantics.browsing.ui.NodeContext;
43 import org.simantics.db.ReadGraph;
44 import org.simantics.db.Resource;
45 import org.simantics.db.common.request.ReadRequest;
46 import org.simantics.db.common.utils.Logger;
47 import org.simantics.db.exception.DatabaseException;
48 import org.simantics.db.layer0.SelectionHints;
49 import org.simantics.db.layer0.adapter.CopyHandler;
50 import org.simantics.db.layer0.util.ClipboardUtils;
51 import org.simantics.db.layer0.util.SimanticsClipboardImpl;
52 import org.simantics.db.layer0.variable.Variable;
53 import org.simantics.ui.utils.ResourceAdaptionUtils;
54 import org.simantics.utils.datastructures.hints.IHintContext;
55 import org.simantics.utils.ui.ISelectionUtils;
56 import org.simantics.utils.ui.SWTUtils;
57 import org.simantics.utils.ui.SWTUtils.ControlFilter;
58 import org.simantics.utils.ui.workbench.WorkbenchUtils;
59
60 public class StandardCopyHandler extends AbstractHandler {
61
62     private static IStatusLineManager status;
63
64     private static List<Variable> getVariables(ISelection selection) {
65         NodeContext context = ISelectionUtils.getSinglePossibleKey(selection, SelectionHints.KEY_MAIN, NodeContext.class);
66         if(context == null) return Collections.emptyList();
67         Object input = context.getConstant(BuiltinKeys.INPUT);
68         IHintContext hints = input instanceof IHintContext ? (IHintContext) input : null;
69         if(hints == null) return Collections.emptyList();
70         Variable var = hints.getHint(SelectionHints.KEY_SELECTION_PROPERTY);
71         if(var == null) return Collections.emptyList();
72         else return Collections.singletonList(var);
73     }
74
75     private boolean copyText(ISelection selection) {
76         if(selection instanceof StructuredSelection) {
77                 StructuredSelection sel = (StructuredSelection)selection;
78                 if(sel.size() == 1) {
79                         Object element = sel.getFirstElement();
80                         if(element instanceof String) {
81                                 setSystemClipboardText((String) element);
82                         }
83                 }
84         }
85         return false;
86     }
87     
88     @Override
89     public Object execute(ExecutionEvent event) throws ExecutionException {
90         
91         status = WorkbenchUtils.getStatusLine( HandlerUtil.getActiveSite(event) );
92         ISelection selection = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().getSelection();
93         
94         // If the selection is plain text copy it into system clipboard and be happy
95         if(copyText(selection)) return null;
96
97         formatSelectionToClipboardText(event);
98         final Resource[] rs = ResourceAdaptionUtils.toResources(selection);
99         Job job = new Job("Copy resources") {
100             
101             @Override
102             protected IStatus run(IProgressMonitor monitor) {
103                 monitor.beginTask("Copy resources to clipboard", 1);
104                 copyResourcesToClipboard(rs, selection, SubMonitor.convert(monitor, 1));
105                 return null;
106             }
107         };
108         job.setUser(true);
109         job.schedule();
110         
111         return null;
112     }
113     
114     public static String copyResourcesToClipboard(final Resource[] rs, ISelection selection, IProgressMonitor monitor) {
115         
116         if(rs == null || rs.length == 0) {
117             // This support was added for copying of properties (variables)
118             final List<Variable> variables = getVariables(selection);
119             if(!variables.isEmpty()) {
120                 final SimanticsClipboardImpl builder = new SimanticsClipboardImpl();
121                 for(Variable var : variables) {
122                     builder.addContent(Collections.singleton(ClipboardUtils.createVariable(Simantics.getSession(), var)), monitor);
123                 }
124                 Simantics.setClipboard(builder);
125                 setCopyMessage(builder.getContents().size(), "variable");
126                 return null;
127             }
128             setCopyMessage(0, "");
129             return null;
130         }
131
132         try {
133             final SimanticsClipboardImpl builder = new SimanticsClipboardImpl();
134             Simantics.getSession().syncRequest(new ReadRequest() {
135                 @Override
136                 public void run(ReadGraph graph) throws DatabaseException {
137                     for (Resource r : rs) {
138                         CopyHandler handler = graph.adapt(r, CopyHandler.class);
139                         handler.copyToClipboard(graph, builder, monitor);
140                     }
141                 }
142             });
143             Simantics.setClipboard(builder);
144             setCopyMessage(builder.getContents().size(), "resource");
145         } catch (DatabaseException e) {
146             Logger.defaultLogError(e);
147         }
148
149         return null;
150     }
151
152     private static void setCopyMessage(int count, String elementName) {
153         if (count > 1)
154             setStatus("Copied " + count + " " + elementName + "s to clipboard");
155         else if (count == 1)
156             setStatus("Copied " + elementName + " to clipboard");
157         else
158             setStatus("Nothing to copy.");
159     }
160
161     private static void setStatus(String message) {
162         if (status != null) {
163             SWTUtils.asyncExec(
164                     PlatformUI.getWorkbench().getDisplay(),
165                     () -> status.setMessage(message));
166         }
167     }
168
169     private boolean formatSelectionToClipboardText(ExecutionEvent event) {
170         Shell shell = HandlerUtil.getActiveShell(event);
171         Tree tree = shell == null ? null : tryGetExplorer(shell.getDisplay().getFocusControl());
172         if (tree == null)
173             return false;
174
175         TreeItem[] selection = tree.getSelection();
176         if (selection.length == 0)
177             return false;
178
179         StringBuilder sb = format(selection, new StringBuilder());
180         if (sb.length() > 0) {
181             setSystemClipboardText(sb.toString());
182             return true;
183         }
184         return false;
185     }
186
187     private static StringBuilder format(TreeItem[] selection, StringBuilder sb) {
188         Set<TreeItem> items = new THashSet<TreeItem>(selection.length);
189         for (TreeItem item : selection)
190             items.add(item);
191         for (TreeItem item : selection) {
192             int cc = item.getParent().getColumnCount();
193             int indent = indentLevel(item, items);
194             for (int i = 0; i < indent; ++i)
195                 sb.append('\t');
196             boolean first = true;
197             for (int c = 0; c < cc; ++c) {
198                 String ct = item.getText(c);
199                 if (!first) {
200                     sb.append('\t');
201                 }
202                 first = false;
203                 sb.append(ct);
204             }
205             sb.append('\n');
206         }
207         return sb;
208     }
209
210     private static int indentLevel(TreeItem item, Set<TreeItem> items) {
211         TreeItem p = item.getParentItem();
212         for (int i = 1; ; p = p.getParentItem()) {
213             if (p == null)
214                 return 0;
215             if (items.contains(p))
216                 return i;
217         }
218     }
219
220     private static Tree tryGetExplorer(Control control) {
221         return SWTUtils.tryGetObject(control, new ControlFilter<Tree>() {
222             @Override
223             public Tree accept(Control control) {
224                 if (!control.isDisposed()
225                         && control instanceof Tree
226                         && control.getData(GraphExplorer.KEY_GRAPH_EXPLORER) != null)
227                     return (Tree) control;
228                 return null;
229             }
230         });
231     }
232
233     private static void setSystemClipboardText(String text) {
234         Clipboard clipboard = new Clipboard(Display.getCurrent());
235         clipboard.setContents(new Object[]{ text }, new Transfer[] { TextTransfer.getInstance() });
236         clipboard.dispose();
237     }
238
239 }