]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsoleView.java
Merge "(refs #7298) Automatic refresh to SCL Console"
[simantics/platform.git] / bundles / org.simantics.scl.ui / src / org / simantics / scl / ui / console / SCLConsoleView.java
1 package org.simantics.scl.ui.console;
2
3 import java.nio.file.Files;
4 import java.nio.file.Path;
5 import java.nio.file.Paths;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.List;
9
10 import org.eclipse.core.runtime.Platform;
11 import org.eclipse.core.runtime.preferences.InstanceScope;
12 import org.eclipse.jface.action.Action;
13 import org.eclipse.jface.action.IAction;
14 import org.eclipse.jface.action.IMenuCreator;
15 import org.eclipse.jface.action.IToolBarManager;
16 import org.eclipse.jface.dialogs.Dialog;
17 import org.eclipse.jface.preference.IPersistentPreferenceStore;
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.dnd.DND;
20 import org.eclipse.swt.dnd.DropTarget;
21 import org.eclipse.swt.dnd.DropTargetAdapter;
22 import org.eclipse.swt.dnd.DropTargetEvent;
23 import org.eclipse.swt.dnd.FileTransfer;
24 import org.eclipse.swt.dnd.Transfer;
25 import org.eclipse.swt.events.SelectionAdapter;
26 import org.eclipse.swt.events.SelectionEvent;
27 import org.eclipse.swt.widgets.Composite;
28 import org.eclipse.swt.widgets.Control;
29 import org.eclipse.swt.widgets.Menu;
30 import org.eclipse.swt.widgets.MenuItem;
31 import org.eclipse.ui.part.ViewPart;
32 import org.eclipse.ui.preferences.ScopedPreferenceStore;
33 import org.simantics.scl.compiler.commands.CommandSessionImportEntry;
34 import org.simantics.scl.compiler.commands.SCLConsoleListener;
35 import org.simantics.scl.compiler.module.repository.UpdateListener;
36 import org.simantics.scl.compiler.testing.TestRunnable;
37 import org.simantics.scl.osgi.internal.TestUtils;
38 import org.simantics.scl.ui.Activator;
39 import org.simantics.scl.ui.imports.internal.ManageImportsDialog;
40 import org.simantics.scl.ui.tests.SCLTestsDialog;
41
42 public class SCLConsoleView extends ViewPart {
43
44     public static final String PLUGIN_ID = "org.simantics.scl.ui";
45     public static final String IMPORTS = "imports";
46     public static final String REFRESH_AUTOMATICALLY = "refresh-automatically";
47     public static final String SEPARATOR = ";";
48     public static final String DISABLED_TAG = "[DISABLED]";
49     
50     IPersistentPreferenceStore store;
51     SCLConsole console;
52     boolean refreshAutomatically = false;
53     MenuItem refreshAutomaticallyItem;
54     
55     private ArrayList<CommandSessionImportEntry> readImportPreferences() {
56         String importsString = store.getString(IMPORTS);
57         
58         String[] splitted = importsString.split(SEPARATOR);
59         ArrayList<CommandSessionImportEntry> result = new ArrayList<CommandSessionImportEntry>(splitted.length);
60         for(String entryString : splitted) {
61             if(entryString.isEmpty())
62                 continue;
63             boolean disabled = false;
64             if(entryString.startsWith(DISABLED_TAG)) {
65                 disabled = true;
66                 entryString = entryString.substring(DISABLED_TAG.length());
67             }
68             String[] parts = entryString.split("=");
69             CommandSessionImportEntry entry;
70             if(parts.length == 1)
71                 entry = new CommandSessionImportEntry(parts[0], "", true);
72             else
73                 entry = new CommandSessionImportEntry(parts[1], parts[0], true);
74             entry.disabled = disabled;
75             result.add(entry);
76         }
77         return result;
78     }
79     
80     private void writeImportPreferences(ArrayList<CommandSessionImportEntry> entries) {
81         StringBuilder b = new StringBuilder();
82         
83         boolean first = true;
84         for(CommandSessionImportEntry entry : entries)
85             if(entry.persistent) {
86                 if(first)
87                     first = false;
88                 else
89                     b.append(SEPARATOR);
90                 if(entry.disabled)
91                     b.append(DISABLED_TAG);
92                 if(!entry.localName.isEmpty()) {
93                     b.append(entry.localName);
94                     b.append("=");
95                 }
96                 b.append(entry.moduleName);
97             }
98         
99         IPersistentPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE, PLUGIN_ID);
100         store.setValue(IMPORTS, b.toString());
101     }
102     
103     private ArrayList<CommandSessionImportEntry> getCurrentImports() {
104         return console.getSession().getImportEntries();
105     }
106     
107     private void setCurrentImports(ArrayList<CommandSessionImportEntry> entries) {
108         console.getSession().setImportEntries(entries);
109     }
110     
111     private void manageImports() {
112         ManageImportsDialog dialog = new ManageImportsDialog(
113                 getSite().getShell(),
114                 getCurrentImports());
115         if(dialog.open() == Dialog.OK) {
116             writeImportPreferences(dialog.getImports());
117             setCurrentImports(dialog.getImports());
118         }
119     }
120     
121     private void sclTestDialog() {
122         List<TestRunnable> tests = TestUtils.getTests();
123         SCLTestsDialog dialog = new SCLTestsDialog(
124                 getSite().getShell(),
125                 tests, true);
126         if(dialog.open() == Dialog.OK) {
127             for(Object result : dialog.getResult()) {
128                 TestRunnable test = (TestRunnable) result;
129                 try {
130                     // Bit of a haxx solution to get around a deadlock caused by simply
131                     // running the test with test.run()
132                     console.execute("import \"Commands/Tests\"");
133                     console.execute("runByName \"" + test.getName() + "\"");
134 //                    test.run();
135                 } catch (Exception e) {
136                     e.printStackTrace();
137                 }
138             }
139         }
140     }
141     
142     private UpdateListener dependencyListener = new UpdateListener() {
143         @Override
144         public void notifyAboutUpdate() {
145             if(refreshAutomatically)
146                 console.getSession().updateRuntimeEnvironment(true);
147         }
148     };
149
150     private void setRefreshAutomatically(boolean refreshAutomatically, boolean refreshAlso) {
151         this.refreshAutomatically = refreshAutomatically;
152         if(refreshAutomaticallyItem != null)
153             refreshAutomaticallyItem.setSelection(refreshAutomatically);
154         
155         store.setValue(REFRESH_AUTOMATICALLY, refreshAutomatically);
156         
157         if(refreshAutomatically) {
158             console.getSession().setDependenciesListener(dependencyListener);
159             if(refreshAlso)
160                 console.getSession().updateRuntimeEnvironment(true);
161         }
162         else {
163             console.getSession().setDependenciesListener(null);
164             dependencyListener.stopListening();
165         }
166     }
167     
168     @Override
169     public void createPartControl(Composite parent) {
170         store = new ScopedPreferenceStore(InstanceScope.INSTANCE, PLUGIN_ID);
171         this.console = new SCLConsole(parent, SWT.NONE);
172         
173         setRefreshAutomatically(store.getBoolean(REFRESH_AUTOMATICALLY), false);
174         setCurrentImports(readImportPreferences());
175
176         addScriptDropSupport(console);
177
178         IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
179         
180         // Interrupt action
181         final Action interruptAction = new Action("Interrupt current command",
182                 Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/stop.png")) {
183             @Override
184             public void run() {
185                 console.interruptCurrentCommands();
186             }
187         };
188         interruptAction.setDisabledImageDescriptor(
189                 Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/stop_disabled.png"));
190         interruptAction.setEnabled(false);
191         toolBarManager.add(interruptAction);
192         
193         // Clear console action
194         final Action clearAction = new Action("Clear console",
195                 Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/clear_console.png")) {
196             @Override
197             public void run() {
198                 setEnabled(false);
199                 console.clear();
200             }
201         };
202         clearAction.setDisabledImageDescriptor(
203                 Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/clear_console_disabled.png"));
204         clearAction.setEnabled(false);
205         toolBarManager.add(clearAction);
206         console.addListener(new SCLConsoleListener() {
207             @Override
208             public void startedExecution() {
209                 interruptAction.setEnabled(true);
210             }
211             @Override
212             public void finishedExecution() {
213                 interruptAction.setEnabled(false);
214             }
215             @Override
216             public void consoleIsNotEmptyAnymore() {
217                 clearAction.setEnabled(true);
218             }
219         });
220         
221         // Refresh action
222         toolBarManager.add(new Action("Refresh modules", IAction.AS_DROP_DOWN_MENU) {
223             {
224                 setImageDescriptor(Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/arrow_refresh.png"));
225                 setMenuCreator(new IMenuCreator() {
226                     Menu menu;
227                     @Override
228                     public Menu getMenu(Menu parent) {
229                         throw new UnsupportedOperationException();
230                     }
231                     
232                     @Override
233                     public Menu getMenu(Control parent) {
234                         if(menu == null) {
235                             menu = new Menu(parent);
236                             refreshAutomaticallyItem = new MenuItem(menu, SWT.CHECK);
237                             refreshAutomaticallyItem.setText("Refresh automatically");
238                             refreshAutomaticallyItem.setSelection(refreshAutomatically);
239                             refreshAutomaticallyItem.addSelectionListener(new SelectionAdapter() {
240                                 @Override
241                                 public void widgetSelected(SelectionEvent e) {
242                                     setRefreshAutomatically(!refreshAutomatically, true);
243                                 }
244                             });
245                         }
246                         return menu;
247                     }
248                     
249                     @Override
250                     public void dispose() {
251                         if(menu != null)
252                             menu.dispose();
253                     }
254                 });
255             }
256             @Override
257             public void run() {
258                 console.getSession().getModuleRepository().getSourceRepository().checkUpdates();
259                 console.getSession().updateRuntimeEnvironment(true);
260                 console.appendOutput("refresh completed\n", console.greenColor, null);
261             }
262         });
263         toolBarManager.add(new Action("Manage imports",
264                 Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/configure_imports.png")) {
265             @Override
266             public void run() {
267                 manageImports();
268             }
269         });
270         
271         // Show action for running SCL tests if in development mode
272         if (Platform.inDevelopmentMode()) {
273             toolBarManager.add(new Action("Run tests",
274                     Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/run_tests.png")) {
275                 @Override
276                 public void run() {
277                     sclTestDialog();
278                 }
279             });
280         }
281         
282         toolBarManager.update(true);
283     }
284
285     private class ScriptRunningDropTarget extends DropTargetAdapter {
286         @Override
287         public void dragEnter(DropTargetEvent event) {
288             if (event.detail == DND.DROP_DEFAULT) {
289                 event.detail = DND.DROP_LINK;
290             }
291         }
292
293         @Override
294         public void dragOperationChanged(DropTargetEvent event) {
295             if (event.detail == DND.DROP_DEFAULT) {
296                 event.detail = DND.DROP_LINK;
297             }
298         }
299
300         public void drop(DropTargetEvent event) {
301             if (FileTransfer.getInstance().isSupportedType(event.currentDataType)) {
302                 String[] files = ((String[]) event.data).clone();
303                 // Sort files by name to allow deterministic multi-file drop
304                 Arrays.sort(files);
305                 for (String file : files) {
306                     Path p = Paths.get(file).toAbsolutePath();
307                     if (isScriptFile(p)) {
308                         console.execute("runFromFile \"" + p.toString().replace('\\', '/') + "\"");
309                     }
310                 }
311             }
312         }
313
314         private boolean isScriptFile(Path p) {
315             return Files.isRegularFile(p)
316                     //&& p.toString().toLowerCase().endsWith(".scl")
317                     ;
318         }
319     }
320
321     private void addScriptDropSupport(SCLConsole console) {
322         DropTarget target = new DropTarget(console.getOutputWidget(), DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK | DND.DROP_DEFAULT);
323         target.setTransfer(new Transfer[] { FileTransfer.getInstance() });
324         target.addDropListener(new ScriptRunningDropTarget());
325     }
326
327     @Override
328     public void setFocus() {
329         console.setFocus();
330     }
331     
332     @Override
333     public void dispose() {
334         super.dispose();
335         console.dispose();
336     }
337
338 }