02fc2d98b6e77265740fe9b12b096961752f1e64
[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.Slider;
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.zoomablecanvas.helper.ZoomableCanvasUserInput;
28 import net.mograsim.logic.core.LogicObserver;
29 import net.mograsim.logic.core.components.CoreClock;
30 import net.mograsim.logic.model.LogicExecuter;
31 import net.mograsim.logic.model.LogicUICanvas;
32 import net.mograsim.machine.Machine;
33 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
34 import net.mograsim.machine.mi.AssignableMicroInstructionMemory;
35 import net.mograsim.plugin.nature.MachineContext;
36 import net.mograsim.plugin.nature.MachineContext.ActiveMachineListener;
37 import net.mograsim.plugin.nature.ProjectMachineContext;
38 import net.mograsim.plugin.tables.DisplaySettings;
39 import net.mograsim.plugin.tables.mi.ActiveInstructionPreviewContentProvider;
40 import net.mograsim.plugin.tables.mi.InstructionTable;
41
42 //TODO what if we open multiple editors?
43 //TODO actually save / load register and latch states
44 public class SimulationViewEditor extends EditorPart
45 {
46         private MachineContext context;
47
48         private LogicExecuter exec;
49         private Machine machine;
50
51         private Composite parent;
52         private Button resetButton;
53         private Button sbseButton;
54         private Button pauseButton;
55         private Label speedFactorLabel;
56         private Slider simSpeedSlider;
57         private Composite canvasParent;
58         private LogicUICanvas canvas;
59         private InstructionTable instPreview;
60         private Label noMachineLabel;
61
62         private ActiveMachineListener activeMachineListener;
63         private MemoryCellModifiedListener memCellListener;
64         private LogicObserver clockObserver;
65
66         public SimulationViewEditor()
67         {
68                 activeMachineListener = m -> recreateContextDependentControls();
69                 memCellListener = a -> instPreview.refresh();
70                 clockObserver = o ->
71                 {
72                         if (((CoreClock) o).isOn())
73                         {
74                                 exec.pauseLiveExecution();
75                                 if (!pauseButton.isDisposed())
76                                         Display.getDefault().asyncExec(() ->
77                                         {
78                                                 if (!pauseButton.isDisposed())
79                                                         pauseButton.setSelection(false);
80                                                 setPauseText(pauseButton, false);
81                                         });
82                         }
83                 };
84         }
85
86         @Override
87         public void createPartControl(Composite parent)
88         {
89                 this.parent = parent;
90                 // initialize UI
91                 parent.setLayout(new GridLayout());
92
93                 noMachineLabel = new Label(parent, SWT.NONE);
94                 noMachineLabel.setText("No machine present...");// TODO internationalize?
95                 addSimulationControlWidgets(parent);
96                 canvasParent = new Composite(parent, SWT.NONE);
97                 canvasParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
98                 canvasParent.setLayout(new FillLayout());
99                 addInstructionPreviewControlWidgets(parent);
100                 recreateContextDependentControls();
101         }
102
103         private void recreateContextDependentControls()
104         {
105                 if (parent == null)
106                         // createPartControls has not been called yet
107                         return;
108
109                 double offX;
110                 double offY;
111                 double zoom;
112                 stopExecAndDeregisterContextDependentListeners();
113                 if (canvas != null)
114                 {
115                         offX = canvas.getOffX();
116                         offY = canvas.getOffY();
117                         zoom = canvas.getZoom();
118                         canvas.dispose();
119                 } else
120                 {
121                         offX = 0;
122                         offY = 0;
123                         zoom = -1;
124                 }
125
126                 Optional<Machine> machineOptional;
127                 if (context != null && (machineOptional = context.getActiveMachine()).isPresent())
128                 {
129                         noMachineLabel.setVisible(false);
130                         resetButton.setEnabled(true);
131                         sbseButton.setEnabled(true);
132                         pauseButton.setEnabled(true);
133                         simSpeedSlider.setEnabled(true);
134
135                         machine = machineOptional.get();
136                         canvas = new LogicUICanvas(canvasParent, SWT.NONE, machine.getModel());
137                         ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(canvas);
138                         userInput.buttonDrag = 3;
139                         userInput.buttonZoom = 2;
140                         userInput.enableUserInput();
141                         if (zoom > 0)
142                         {
143                                 canvas.moveTo(offX, offY, zoom);
144                                 canvas.commitTransform();
145                         }
146
147                         AssignableMicroInstructionMemory mIMemory = machine.getMicroInstructionMemory();
148                         instPreview.bindMicroInstructionMemory(mIMemory);
149                         mIMemory.registerCellModifiedListener(memCellListener);
150
151                         canvasParent.layout();
152
153                         // initialize executer
154                         exec = new LogicExecuter(machine.getTimeline());
155                         updateSpeedFactor();
156                         updatePausedState();
157                         exec.startLiveExecution();
158                 } else
159                 {
160                         noMachineLabel.setVisible(true);
161                         resetButton.setEnabled(false);
162                         sbseButton.setEnabled(false);
163                         pauseButton.setEnabled(false);
164                         simSpeedSlider.setEnabled(false);
165                 }
166         }
167
168         private void stopExecAndDeregisterContextDependentListeners()
169         {
170                 if (exec != null)
171                         exec.stopLiveExecution();
172                 if (machine != null)
173                 {
174                         machine.getMicroInstructionMemory().deregisterCellModifiedListener(memCellListener);
175                         machine.getClock().deregisterObserver(clockObserver);
176                 }
177         }
178
179         private void addSimulationControlWidgets(Composite parent)
180         {
181                 Composite c = new Composite(parent, SWT.NONE);
182                 c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
183                 c.setLayout(new GridLayout(7, false));
184
185                 resetButton = new Button(c, SWT.PUSH);
186                 resetButton.setText("Reset machine");
187                 resetButton.addListener(SWT.Selection, e -> context.getActiveMachine().get().reset());
188
189                 sbseButton = new Button(c, SWT.CHECK);
190                 pauseButton = new Button(c, SWT.TOGGLE);
191
192                 sbseButton.setText("Step by step execution");
193                 sbseButton.addListener(SWT.Selection, e ->
194                 {
195                         CoreClock cl = machine.getClock();
196                         if (sbseButton.getSelection())
197                                 cl.registerObserver(clockObserver);
198                         else
199                                 cl.deregisterObserver(clockObserver);
200                 });
201                 sbseButton.setSelection(false);
202
203                 pauseButton.setSelection(true);
204                 setPauseText(pauseButton, false);
205
206                 pauseButton.addListener(SWT.Selection, e -> updatePausedState());
207                 pauseButton.addMouseTrackListener(new MouseTrackListener()
208                 {
209                         @Override
210                         public void mouseHover(MouseEvent e)
211                         {
212                                 // nothing
213                         }
214
215                         @Override
216                         public void mouseExit(MouseEvent e)
217                         {
218                                 setPauseText(pauseButton, false);
219                         }
220
221                         @Override
222                         public void mouseEnter(MouseEvent e)
223                         {
224                                 setPauseText(pauseButton, true);
225                         }
226                 });
227
228                 new Label(c, SWT.NONE).setText("Simulation Speed: ");
229
230                 simSpeedSlider = new Slider(c, SWT.NONE);
231                 simSpeedSlider.setMinimum(0);
232                 simSpeedSlider.setMaximum(50 + simSpeedSlider.getThumb());
233                 simSpeedSlider.setIncrement(1);
234                 simSpeedSlider.setSelection(0);
235
236                 speedFactorLabel = new Label(c, SWT.NONE);
237                 speedFactorLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
238
239                 simSpeedSlider.addListener(SWT.Selection, e -> updateSpeedFactor());
240                 updateSpeedFactor();
241
242                 c.layout();
243         }
244
245         private void updatePausedState()
246         {
247                 setPauseText(pauseButton, false);
248                 if (exec != null)
249                         if (pauseButton.getSelection())
250                                 exec.unpauseLiveExecution();
251                         else
252                                 exec.pauseLiveExecution();
253         }
254
255         private void updateSpeedFactor()
256         {
257                 double factor = Math.pow(1.32, simSpeedSlider.getSelection() - 50);
258                 speedFactorLabel.setText(String.format("%f", factor));
259                 if (exec != null)
260                         exec.setSpeedFactor(factor);
261         }
262
263         private void addInstructionPreviewControlWidgets(Composite parent)
264         {
265                 instPreview = new InstructionTable(parent, new DisplaySettings());
266                 instPreview.getTableViewer().getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
267                 instPreview.setContentProvider(new ActiveInstructionPreviewContentProvider(instPreview.getTableViewer()));
268         }
269
270         private static void setPauseText(Button pauseButton, boolean hovered)
271         {
272                 if (hovered)
273                         if (pauseButton.getSelection())
274                                 pauseButton.setText("Pause?");
275                         else
276                                 pauseButton.setText("Resume?");
277                 else if (pauseButton.getSelection())
278                         pauseButton.setText("Running");
279                 else
280                         pauseButton.setText("Paused");
281         }
282
283         @Override
284         public void init(IEditorSite site, IEditorInput input) throws PartInitException
285         {
286                 if (input instanceof IFileEditorInput)
287                 {
288                         IFileEditorInput fileInput = (IFileEditorInput) input;
289                         context = ProjectMachineContext.getMachineContextOf(fileInput.getFile().getProject());
290                         context.activateMachine();
291                         context.addActiveMachineListener(activeMachineListener);
292                         recreateContextDependentControls();
293
294                         setPartName(fileInput.getName());
295                         open(fileInput.getFile());
296                 } else
297                         throw new IllegalArgumentException("SimulationViewEditor can only be used with Files");
298
299                 setSite(site);
300                 setInput(input);
301         }
302
303         @Override
304         public void doSave(IProgressMonitor monitor)
305         {
306                 IEditorInput input = getEditorInput();
307                 if (input instanceof IFileEditorInput)
308                         SafeRunnable.getRunner().run(() -> save(((IFileEditorInput) input).getFile(), monitor));
309                 else
310                         throw new IllegalArgumentException("SimulationViewEditor can only be used with Files");
311         }
312
313         private void save(IFile file, IProgressMonitor monitor) throws CoreException
314         {
315                 file.setContents(new ByteArrayInputStream("actual contents will go here".getBytes()), 0, monitor);
316         }
317
318         private void open(IFile file)
319         {
320                 // do nothing yet
321         }
322
323         @Override
324         public void doSaveAs()
325         {
326                 throw new UnsupportedOperationException();
327         }
328
329         @Override
330         public boolean isDirty()
331         {
332                 return false;
333         }
334
335         @Override
336         public boolean isSaveAsAllowed()
337         {
338                 return false;
339         }
340
341         @Override
342         public void setFocus()
343         {
344                 canvas.setFocus();
345         }
346
347         @Override
348         public void dispose()
349         {
350                 stopExecAndDeregisterContextDependentListeners();
351                 context.removeActiveMachineListener(activeMachineListener);
352                 super.dispose();
353         }
354 }