1 package net.mograsim.plugin.editors;
3 import java.io.ByteArrayInputStream;
4 import java.util.Optional;
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;
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;
45 //TODO what if we open multiple editors?
46 //TODO actually save / load register and latch states
47 public class SimulationViewEditor extends EditorPart
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);
53 private MachineContext context;
55 private LogicExecuter exec;
56 private Machine machine;
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;
69 private ActiveMachineListener activeMachineListener;
70 private MemoryCellModifiedListener memCellListener;
71 private LogicObserver clockObserver;
73 public SimulationViewEditor()
75 activeMachineListener = m -> recreateContextDependentControls();
76 memCellListener = a -> instPreview.refresh();
79 if (((CoreClock) o).isOn())
81 exec.pauseLiveExecution();
82 if (!pauseButton.isDisposed())
83 Display.getDefault().asyncExec(() ->
85 if (!pauseButton.isDisposed())
86 pauseButton.setSelection(false);
87 setPauseText(pauseButton, false);
94 public void createPartControl(Composite parent)
98 parent.setLayout(new GridLayout());
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();
110 private void recreateContextDependentControls()
113 // createPartControls has not been called yet
119 stopExecAndDeregisterContextDependentListeners();
122 offX = canvas.getOffX();
123 offY = canvas.getOffY();
124 zoom = canvas.getZoom();
133 Optional<Machine> machineOptional;
134 if (context != null && (machineOptional = context.getActiveMachine()).isPresent())
136 noMachineLabel.setVisible(false);
137 resetButton.setEnabled(true);
138 sbseButton.setEnabled(true);
139 pauseButton.setEnabled(true);
140 simSpeedScale.setEnabled(true);
141 simSpeedInput.setEnabled(true);
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();
152 canvas.moveTo(offX, offY, zoom);
153 canvas.commitTransform();
156 AssignableMicroInstructionMemory mIMemory = machine.getMicroInstructionMemory();
157 instPreview.bindMicroInstructionMemory(mIMemory);
158 mIMemory.registerCellModifiedListener(memCellListener);
160 canvasParent.layout();
162 // initialize executer
163 exec = new LogicExecuter(machine.getTimeline());
164 updateSpeedFactorFromInput(simSpeedInput.getValue());
166 exec.startLiveExecution();
169 noMachineLabel.setVisible(true);
170 resetButton.setEnabled(false);
171 sbseButton.setEnabled(false);
172 pauseButton.setEnabled(false);
173 simSpeedScale.setEnabled(false);
174 simSpeedInput.setEnabled(false);
178 private void stopExecAndDeregisterContextDependentListeners()
181 exec.stopLiveExecution();
184 machine.getMicroInstructionMemory().deregisterCellModifiedListener(memCellListener);
185 machine.getClock().deregisterObserver(clockObserver);
189 private void addSimulationControlWidgets(Composite parent)
191 Composite c = new Composite(parent, SWT.NONE);
192 c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
193 c.setLayout(new GridLayout(7, false));
195 resetButton = new Button(c, SWT.PUSH);
196 resetButton.setText("Reset machine");
197 resetButton.addListener(SWT.Selection, e -> context.getActiveMachine().get().reset());
199 // TODO do we want this button in the final product?
200 Button reloadMachineButton = new Button(c, SWT.PUSH);
201 reloadMachineButton.setText("Reload machine");
202 reloadMachineButton.addListener(SWT.Selection, e ->
204 IndirectModelComponentCreator.clearComponentCache();
205 context.setActiveMachine(context.getMachineDefinition().get().createNew());
208 sbseButton = new Button(c, SWT.CHECK);
209 pauseButton = new Button(c, SWT.TOGGLE);
211 sbseButton.setText("Step by step execution");
212 sbseButton.addListener(SWT.Selection, e ->
214 CoreClock cl = machine.getClock();
215 if (sbseButton.getSelection())
216 cl.registerObserver(clockObserver);
218 cl.deregisterObserver(clockObserver);
220 sbseButton.setSelection(false);
222 pauseButton.setSelection(true);
223 setPauseText(pauseButton, false);
225 pauseButton.addListener(SWT.Selection, e -> updatePausedState());
226 pauseButton.addMouseTrackListener(new MouseTrackListener()
229 public void mouseHover(MouseEvent e)
235 public void mouseExit(MouseEvent e)
237 setPauseText(pauseButton, false);
241 public void mouseEnter(MouseEvent e)
243 setPauseText(pauseButton, true);
247 new Label(c, SWT.NONE).setText("Simulation Speed: ");
249 simSpeedScale = new Scale(c, SWT.NONE);
250 simSpeedScale.setMinimum(0);
251 simSpeedScale.setMaximum(SIM_SPEED_SCALE_STEPS);
252 simSpeedScale.setIncrement(1);
253 simSpeedScale.setSelection(0);
254 simSpeedScale.addListener(SWT.Selection, e -> updateSpeedFactorFromScale());
256 simSpeedInput = new DoubleInput(c, SWT.NONE);
257 simSpeedInput.setPrecision(Preferences.current().getInt("net.mograsim.plugin.core.simspeedprecision"));
258 simSpeedInput.addChangeListener(this::updateSpeedFactorFromInput);
260 updateSpeedFactorFromScale();
265 private void updatePausedState()
267 setPauseText(pauseButton, false);
269 if (pauseButton.getSelection())
270 exec.unpauseLiveExecution();
272 exec.pauseLiveExecution();
275 private void updateSpeedFactorFromScale()
277 double factor = Math.pow(SIM_SPEED_SCALE_STEP_FACTOR, simSpeedScale.getSelection() - SIM_SPEED_SCALE_STEPS);
278 simSpeedInput.setValue(factor);
280 exec.setSpeedFactor(factor);
283 private void updateSpeedFactorFromInput(double factor)
285 double factorCheckedFor0;
287 factorCheckedFor0 = factor;
290 factorCheckedFor0 = Math.pow(10, -simSpeedInput.getPrecision());
291 simSpeedInput.setValue(factorCheckedFor0);
293 int closestScalePos = (int) Math.round(Math.log(factorCheckedFor0) / SIM_SPEED_SCALE_STEP_FACTOR_LOG + SIM_SPEED_SCALE_STEPS);
294 simSpeedScale.setSelection(Math.min(Math.max(closestScalePos, 0), SIM_SPEED_SCALE_STEPS));
296 exec.setSpeedFactor(factorCheckedFor0);
299 private void addInstructionPreviewControlWidgets(Composite parent)
301 instPreview = new InstructionTable(parent, new DisplaySettings());
302 instPreview.getTableViewer().getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
303 instPreview.setContentProvider(new ActiveInstructionPreviewContentProvider(instPreview.getTableViewer()));
306 private static void setPauseText(Button pauseButton, boolean hovered)
309 if (pauseButton.getSelection())
310 pauseButton.setText("Pause?");
312 pauseButton.setText("Resume?");
313 else if (pauseButton.getSelection())
314 pauseButton.setText("Running");
316 pauseButton.setText("Paused");
320 public void init(IEditorSite site, IEditorInput input) throws PartInitException
322 if (input instanceof IFileEditorInput)
324 IFileEditorInput fileInput = (IFileEditorInput) input;
325 context = ProjectMachineContext.getMachineContextOf(fileInput.getFile().getProject());
326 context.activateMachine();
327 context.addActiveMachineListener(activeMachineListener);
328 recreateContextDependentControls();
330 setPartName(fileInput.getName());
331 open(fileInput.getFile());
333 throw new IllegalArgumentException("SimulationViewEditor can only be used with Files");
340 public void doSave(IProgressMonitor monitor)
342 IEditorInput input = getEditorInput();
343 if (input instanceof IFileEditorInput)
344 SafeRunnable.getRunner().run(() -> save(((IFileEditorInput) input).getFile(), monitor));
346 throw new IllegalArgumentException("SimulationViewEditor can only be used with Files");
349 private void save(IFile file, IProgressMonitor monitor) throws CoreException
351 file.setContents(new ByteArrayInputStream("actual contents will go here".getBytes()), 0, monitor);
354 private void open(IFile file)
360 public void doSaveAs()
362 throw new UnsupportedOperationException();
366 public boolean isDirty()
372 public boolean isSaveAsAllowed()
378 public void setFocus()
384 public void dispose()
386 stopExecAndDeregisterContextDependentListeners();
387 context.removeActiveMachineListener(activeMachineListener);