Fixed crashbug in ActiveInstructionPreviewContentProvider
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / editors / SimulationViewEditor.java
1 package net.mograsim.plugin.editors;
2
3 import java.io.ByteArrayInputStream;
4 import java.util.Optional;
5
6 import org.eclipse.core.resources.IFile;
7 import org.eclipse.core.runtime.CoreException;
8 import org.eclipse.core.runtime.IProgressMonitor;
9 import org.eclipse.jface.util.SafeRunnable;
10 import org.eclipse.swt.SWT;
11 import org.eclipse.swt.events.MouseEvent;
12 import org.eclipse.swt.events.MouseTrackListener;
13 import org.eclipse.swt.layout.FillLayout;
14 import org.eclipse.swt.layout.GridData;
15 import org.eclipse.swt.layout.GridLayout;
16 import org.eclipse.swt.widgets.Button;
17 import org.eclipse.swt.widgets.Composite;
18 import org.eclipse.swt.widgets.Display;
19 import org.eclipse.swt.widgets.Label;
20 import org.eclipse.swt.widgets.Scale;
21 import org.eclipse.ui.IEditorInput;
22 import org.eclipse.ui.IEditorSite;
23 import org.eclipse.ui.IFileEditorInput;
24 import org.eclipse.ui.PartInitException;
25 import org.eclipse.ui.part.EditorPart;
26
27 import net.haspamelodica.swt.helper.input.DoubleInput;
28 import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
29 import net.mograsim.logic.core.LogicObserver;
30 import net.mograsim.logic.core.components.CoreClock;
31 import net.mograsim.logic.model.LogicExecuter;
32 import net.mograsim.logic.model.LogicUICanvas;
33 import net.mograsim.logic.model.serializing.IndirectModelComponentCreator;
34 import net.mograsim.machine.Machine;
35 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
36 import net.mograsim.machine.mi.AssignableMicroInstructionMemory;
37 import net.mograsim.plugin.nature.MachineContext;
38 import net.mograsim.plugin.nature.MachineContext.ActiveMachineListener;
39 import net.mograsim.plugin.nature.ProjectMachineContext;
40 import net.mograsim.plugin.tables.DisplaySettings;
41 import net.mograsim.plugin.tables.mi.ActiveInstructionPreviewContentProvider;
42 import net.mograsim.plugin.tables.mi.InstructionTable;
43 import net.mograsim.preferences.Preferences;
44
45 //TODO what if we open multiple editors?
46 //TODO actually save / load register and latch states
47 public class SimulationViewEditor extends EditorPart
48 {
49         private static final int SIM_SPEED_SCALE_STEPS = 50;
50         private static final double SIM_SPEED_SCALE_STEP_FACTOR = 1.32;
51         private static final double SIM_SPEED_SCALE_STEP_FACTOR_LOG = Math.log(SIM_SPEED_SCALE_STEP_FACTOR);
52
53         private MachineContext context;
54
55         private LogicExecuter exec;
56         private Machine machine;
57
58         private Composite parent;
59         private Button resetButton;
60         private Button sbseButton;
61         private Button pauseButton;
62         private Scale simSpeedScale;
63         private DoubleInput simSpeedInput;
64         private Composite canvasParent;
65         private LogicUICanvas canvas;
66         private InstructionTable instPreview;
67         private Label noMachineLabel;
68
69         private ActiveMachineListener activeMachineListener;
70         private MemoryCellModifiedListener memCellListener;
71         private LogicObserver clockObserver;
72
73         public SimulationViewEditor()
74         {
75                 activeMachineListener = (oldM, newM) -> recreateContextDependentControls();
76                 memCellListener = a -> instPreview.refresh();
77                 clockObserver = o ->
78                 {
79                         if (((CoreClock) o).isOn())
80                         {
81                                 exec.pauseLiveExecution();
82                                 if (!pauseButton.isDisposed())
83                                         Display.getDefault().asyncExec(() ->
84                                         {
85                                                 if (!pauseButton.isDisposed())
86                                                         pauseButton.setSelection(false);
87                                                 setPauseText(pauseButton, false);
88                                         });
89                         }
90                 };
91         }
92
93         @Override
94         public void createPartControl(Composite parent)
95         {
96                 this.parent = parent;
97                 // initialize UI
98                 parent.setLayout(new GridLayout());
99
100                 noMachineLabel = new Label(parent, SWT.NONE);
101                 noMachineLabel.setText("No machine present...");// TODO internationalize?
102                 addSimulationControlWidgets(parent);
103                 canvasParent = new Composite(parent, SWT.NONE);
104                 canvasParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
105                 canvasParent.setLayout(new FillLayout());
106                 addInstructionPreviewControlWidgets(parent);
107                 recreateContextDependentControls();
108         }
109
110         private void recreateContextDependentControls()
111         {
112                 if (parent == null)
113                         // createPartControls has not been called yet
114                         return;
115
116                 double offX;
117                 double offY;
118                 double zoom;
119                 stopExecAndDeregisterContextDependentListeners();
120                 if (canvas != null)
121                 {
122                         offX = canvas.getOffX();
123                         offY = canvas.getOffY();
124                         zoom = canvas.getZoom();
125                         canvas.dispose();
126                 } else
127                 {
128                         offX = 0;
129                         offY = 0;
130                         zoom = -1;
131                 }
132
133                 Optional<Machine> machineOptional;
134                 if (context != null && (machineOptional = context.getActiveMachine()).isPresent())
135                 {
136                         noMachineLabel.setVisible(false);
137                         resetButton.setEnabled(true);
138                         sbseButton.setEnabled(true);
139                         pauseButton.setEnabled(true);
140                         simSpeedScale.setEnabled(true);
141                         simSpeedInput.setEnabled(true);
142
143                         machine = machineOptional.get();
144                         canvas = new LogicUICanvas(canvasParent, SWT.NONE, machine.getModel());
145                         canvas.addListener(SWT.MouseDown, e -> canvas.setFocus());
146                         ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(canvas);
147                         userInput.buttonDrag = Preferences.current().getInt("net.mograsim.logic.model.button.drag");
148                         userInput.buttonZoom = Preferences.current().getInt("net.mograsim.logic.model.button.zoom");
149                         userInput.enableUserInput();
150                         if (zoom > 0)
151                         {
152                                 canvas.moveTo(offX, offY, zoom);
153                                 canvas.commitTransform();
154                         }
155
156                         AssignableMicroInstructionMemory mIMemory = machine.getMicroInstructionMemory();
157                         instPreview.bindMicroInstructionMemory(mIMemory);
158                         mIMemory.registerCellModifiedListener(memCellListener);
159
160                         canvasParent.layout();
161
162                         // update preview
163                         ((ActiveInstructionPreviewContentProvider) instPreview.getTableViewer().getContentProvider()).setMachine(machine);
164
165                         // initialize executer
166                         exec = new LogicExecuter(machine.getTimeline());
167                         updateSpeedFactorFromInput(simSpeedInput.getValue());
168                         updatePausedState();
169                         exec.startLiveExecution();
170                 } else
171                 {
172                         noMachineLabel.setVisible(true);
173                         resetButton.setEnabled(false);
174                         sbseButton.setEnabled(false);
175                         pauseButton.setEnabled(false);
176                         simSpeedScale.setEnabled(false);
177                         simSpeedInput.setEnabled(false);
178                 }
179         }
180
181         private void stopExecAndDeregisterContextDependentListeners()
182         {
183                 if (exec != null)
184                         exec.stopLiveExecution();
185                 if (machine != null)
186                 {
187                         machine.getMicroInstructionMemory().deregisterCellModifiedListener(memCellListener);
188                         machine.getClock().deregisterObserver(clockObserver);
189                 }
190         }
191
192         private void addSimulationControlWidgets(Composite parent)
193         {
194                 Composite c = new Composite(parent, SWT.NONE);
195                 c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
196                 c.setLayout(new GridLayout(7, false));
197
198                 resetButton = new Button(c, SWT.PUSH);
199                 resetButton.setText("Reset machine");
200                 resetButton.addListener(SWT.Selection, e -> context.getActiveMachine().get().reset());
201
202                 // TODO do we want this button in the final product?
203                 Button reloadMachineButton = new Button(c, SWT.PUSH);
204                 reloadMachineButton.setText("Reload machine");
205                 reloadMachineButton.addListener(SWT.Selection, e ->
206                 {
207                         IndirectModelComponentCreator.clearComponentCache();
208                         context.setActiveMachine(context.getMachineDefinition().get().createNew());
209                 });
210
211                 sbseButton = new Button(c, SWT.CHECK);
212                 pauseButton = new Button(c, SWT.TOGGLE);
213
214                 sbseButton.setText("Step by step execution");
215                 sbseButton.addListener(SWT.Selection, e ->
216                 {
217                         CoreClock cl = machine.getClock();
218                         if (sbseButton.getSelection())
219                                 cl.registerObserver(clockObserver);
220                         else
221                                 cl.deregisterObserver(clockObserver);
222                 });
223                 sbseButton.setSelection(false);
224
225                 pauseButton.setSelection(true);
226                 setPauseText(pauseButton, false);
227
228                 pauseButton.addListener(SWT.Selection, e -> updatePausedState());
229                 pauseButton.addMouseTrackListener(new MouseTrackListener()
230                 {
231                         @Override
232                         public void mouseHover(MouseEvent e)
233                         {
234                                 // nothing
235                         }
236
237                         @Override
238                         public void mouseExit(MouseEvent e)
239                         {
240                                 setPauseText(pauseButton, false);
241                         }
242
243                         @Override
244                         public void mouseEnter(MouseEvent e)
245                         {
246                                 setPauseText(pauseButton, true);
247                         }
248                 });
249
250                 new Label(c, SWT.NONE).setText("Simulation Speed: ");
251
252                 simSpeedScale = new Scale(c, SWT.NONE);
253                 simSpeedScale.setMinimum(0);
254                 simSpeedScale.setMaximum(SIM_SPEED_SCALE_STEPS);
255                 simSpeedScale.setIncrement(1);
256                 simSpeedScale.setSelection(0);
257                 simSpeedScale.addListener(SWT.Selection, e -> updateSpeedFactorFromScale());
258
259                 simSpeedInput = new DoubleInput(c, SWT.NONE);
260                 simSpeedInput.setPrecision(Preferences.current().getInt("net.mograsim.plugin.core.simspeedprecision"));
261                 simSpeedInput.addChangeListener(this::updateSpeedFactorFromInput);
262
263                 updateSpeedFactorFromScale();
264
265                 c.layout();
266         }
267
268         private void updatePausedState()
269         {
270                 setPauseText(pauseButton, false);
271                 if (exec != null)
272                         if (pauseButton.getSelection())
273                                 exec.unpauseLiveExecution();
274                         else
275                                 exec.pauseLiveExecution();
276         }
277
278         private void updateSpeedFactorFromScale()
279         {
280                 double factor = Math.pow(SIM_SPEED_SCALE_STEP_FACTOR, simSpeedScale.getSelection() - SIM_SPEED_SCALE_STEPS);
281                 simSpeedInput.setValue(factor);
282                 if (exec != null)
283                         exec.setSpeedFactor(factor);
284         }
285
286         private void updateSpeedFactorFromInput(double factor)
287         {
288                 double factorCheckedFor0;
289                 if (factor != 0)
290                         factorCheckedFor0 = factor;
291                 else
292                 {
293                         factorCheckedFor0 = Math.pow(10, -simSpeedInput.getPrecision());
294                         simSpeedInput.setValue(factorCheckedFor0);
295                 }
296                 int closestScalePos = (int) Math.round(Math.log(factorCheckedFor0) / SIM_SPEED_SCALE_STEP_FACTOR_LOG + SIM_SPEED_SCALE_STEPS);
297                 simSpeedScale.setSelection(Math.min(Math.max(closestScalePos, 0), SIM_SPEED_SCALE_STEPS));
298                 if (exec != null)
299                         exec.setSpeedFactor(factorCheckedFor0);
300         }
301
302         private void addInstructionPreviewControlWidgets(Composite parent)
303         {
304                 instPreview = new InstructionTable(parent, new DisplaySettings(), getSite().getWorkbenchWindow().getWorkbench().getThemeManager());
305                 instPreview.getTableViewer().getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
306                 ActiveInstructionPreviewContentProvider cProv;
307                 instPreview.setContentProvider(cProv = new ActiveInstructionPreviewContentProvider(instPreview.getTableViewer()));
308                 cProv.setMachine(machine);
309         }
310
311         private static void setPauseText(Button pauseButton, boolean hovered)
312         {
313                 if (hovered)
314                         if (pauseButton.getSelection())
315                                 pauseButton.setText("Pause?");
316                         else
317                                 pauseButton.setText("Resume?");
318                 else if (pauseButton.getSelection())
319                         pauseButton.setText("Running");
320                 else
321                         pauseButton.setText("Paused");
322         }
323
324         @Override
325         public void init(IEditorSite site, IEditorInput input) throws PartInitException
326         {
327                 if (input instanceof IFileEditorInput)
328                 {
329                         IFileEditorInput fileInput = (IFileEditorInput) input;
330                         context = ProjectMachineContext.getMachineContextOf(fileInput.getFile().getProject());
331                         context.activateMachine();
332                         context.addActiveMachineListener(activeMachineListener);
333                         recreateContextDependentControls();
334
335                         setPartName(fileInput.getName());
336                         open(fileInput.getFile());
337                 } else
338                         throw new IllegalArgumentException("SimulationViewEditor can only be used with Files");
339
340                 setSite(site);
341                 setInput(input);
342         }
343
344         @Override
345         public void doSave(IProgressMonitor monitor)
346         {
347                 IEditorInput input = getEditorInput();
348                 if (input instanceof IFileEditorInput)
349                         SafeRunnable.getRunner().run(() -> save(((IFileEditorInput) input).getFile(), monitor));
350                 else
351                         throw new IllegalArgumentException("SimulationViewEditor can only be used with Files");
352         }
353
354         private void save(IFile file, IProgressMonitor monitor) throws CoreException
355         {
356                 file.setContents(new ByteArrayInputStream("actual contents will go here".getBytes()), 0, monitor);
357         }
358
359         private void open(IFile file)
360         {
361                 // do nothing yet
362         }
363
364         @Override
365         public void doSaveAs()
366         {
367                 throw new UnsupportedOperationException();
368         }
369
370         @Override
371         public boolean isDirty()
372         {
373                 return false;
374         }
375
376         @Override
377         public boolean isSaveAsAllowed()
378         {
379                 return false;
380         }
381
382         @Override
383         public void setFocus()
384         {
385                 canvas.setFocus();
386         }
387
388         @Override
389         public void dispose()
390         {
391                 stopExecAndDeregisterContextDependentListeners();
392                 context.removeActiveMachineListener(activeMachineListener);
393                 super.dispose();
394         }
395 }