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