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