Moved logic for extracting DebugTarget from context change to a Listener
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / views / SimulationView.java
1 package net.mograsim.plugin.views;
2
3 import java.util.HashSet;
4 import java.util.Optional;
5 import java.util.Set;
6 import java.util.function.Consumer;
7
8 import org.eclipse.core.runtime.SafeRunner;
9 import org.eclipse.debug.ui.DebugUITools;
10 import org.eclipse.debug.ui.contexts.IDebugContextManager;
11 import org.eclipse.debug.ui.contexts.IDebugContextService;
12 import org.eclipse.swt.SWT;
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.Control;
19 import org.eclipse.swt.widgets.Label;
20 import org.eclipse.swt.widgets.Scale;
21 import org.eclipse.ui.PlatformUI;
22 import org.eclipse.ui.part.ViewPart;
23
24 import net.haspamelodica.swt.helper.input.DoubleInput;
25 import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
26 import net.mograsim.logic.core.LogicObserver;
27 import net.mograsim.logic.core.components.CoreClock;
28 import net.mograsim.logic.model.LogicUICanvas;
29 import net.mograsim.machine.Machine;
30 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
31 import net.mograsim.machine.mi.AssignableMicroInstructionMemory;
32 import net.mograsim.plugin.launch.MachineDebugContextListener;
33 import net.mograsim.plugin.launch.MachineDebugTarget;
34 import net.mograsim.plugin.tables.DisplaySettings;
35 import net.mograsim.plugin.tables.mi.ActiveInstructionPreviewContentProvider;
36 import net.mograsim.plugin.tables.mi.InstructionTable;
37 import net.mograsim.plugin.util.OverlappingFillLayout;
38 import net.mograsim.preferences.Preferences;
39
40 public class SimulationView extends ViewPart
41 {
42         private static final int SIM_SPEED_SCALE_STEPS = 50;
43         private static final double SIM_SPEED_SCALE_STEP_FACTOR = 1.32;
44         private static final double SIM_SPEED_SCALE_STEP_FACTOR_LOG = Math.log(SIM_SPEED_SCALE_STEP_FACTOR);
45
46         private final Set<Control> controlsToDisableWhenNoMachinePresent;
47         private Button sbseButton;
48         private Scale simSpeedScale;
49         private DoubleInput simSpeedInput;
50         private Composite contextDependentControlsParent;
51         private Composite canvasParent;
52         private InstructionTable instPreview;
53         private ActiveInstructionPreviewContentProvider contentProvider;
54         private Label noRunningMachineLabel;
55
56         private MachineDebugTarget debugTarget;
57         private LogicUICanvas canvas;
58
59         private final MemoryCellModifiedListener memCellListener;
60         private final LogicObserver clockObserver;
61         private final MachineDebugContextListener debugContextListener;
62         private final Consumer<Double> executionSpeedListener;
63
64         public SimulationView()
65         {
66                 controlsToDisableWhenNoMachinePresent = new HashSet<>();
67                 memCellListener = a -> instPreview.refresh();
68                 // TODO could this be a breakpoint?
69                 clockObserver = o ->
70                 {
71                         if (((CoreClock) o).isOn())
72                                 SafeRunner.run(() -> debugTarget.suspend());
73                 };
74                 debugContextListener = new MachineDebugContextListener()
75                 {
76                         @Override
77                         public void machineDebugContextChanged(Optional<MachineDebugTarget> oldTarget, Optional<MachineDebugTarget> newTarget)
78                         {
79                                 SimulationView.this.debugContextChanged(newTarget);
80                         }
81                 };
82                 executionSpeedListener = this::speedFactorChanged;
83         }
84
85         @Override
86         public void createPartControl(Composite parent)
87         {
88                 // initialize UI
89                 parent.setLayout(new GridLayout());
90
91                 addSimulationControlWidgets(parent);
92
93                 Composite contextDependentControlsParentParent = new Composite(parent, SWT.NONE);
94                 contextDependentControlsParentParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
95                 contextDependentControlsParentParent.setLayout(new OverlappingFillLayout());
96
97                 noRunningMachineLabel = new Label(contextDependentControlsParentParent, SWT.NONE);
98                 noRunningMachineLabel.setText("No machine running && selected in the Debug view...");
99
100                 contextDependentControlsParent = new Composite(contextDependentControlsParentParent, SWT.NONE);
101                 GridLayout contexDependentControlsLayout = new GridLayout();
102                 contexDependentControlsLayout.marginWidth = 0;
103                 contexDependentControlsLayout.marginHeight = 0;
104                 contextDependentControlsParent.setLayout(contexDependentControlsLayout);
105
106                 canvasParent = new Composite(contextDependentControlsParent, SWT.NONE);
107                 canvasParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
108                 canvasParent.setLayout(new FillLayout());
109
110                 addInstructionPreviewControlWidgets(contextDependentControlsParent);
111
112                 IDebugContextManager debugCManager = DebugUITools.getDebugContextManager();
113                 IDebugContextService contextService = debugCManager.getContextService(PlatformUI.getWorkbench().getActiveWorkbenchWindow());
114                 contextService.addDebugContextListener(debugContextListener);
115                 debugContextListener.debugContextChanged(contextService.getActiveContext());
116         }
117
118         private void addSimulationControlWidgets(Composite parent)
119         {
120                 Composite c = new Composite(parent, SWT.NONE);
121                 c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
122                 c.setLayout(new GridLayout(7, false));
123
124                 sbseButton = new Button(c, SWT.CHECK);
125                 controlsToDisableWhenNoMachinePresent.add(sbseButton);
126
127                 sbseButton.setText("Step by step execution");
128                 sbseButton.addListener(SWT.Selection, e ->
129                 {
130                         CoreClock cl = debugTarget.getMachine().getClock();
131                         if (sbseButton.getSelection())
132                                 cl.registerObserver(clockObserver);
133                         else
134                                 cl.deregisterObserver(clockObserver);
135                 });
136                 sbseButton.setSelection(false);
137
138                 Label simSpeedLabel = new Label(c, SWT.NONE);
139                 controlsToDisableWhenNoMachinePresent.add(simSpeedLabel);
140                 simSpeedLabel.setText("Simulation Speed: ");
141
142                 simSpeedScale = new Scale(c, SWT.NONE);
143                 controlsToDisableWhenNoMachinePresent.add(simSpeedScale);
144                 simSpeedScale.setMinimum(0);
145                 simSpeedScale.setMaximum(SIM_SPEED_SCALE_STEPS);
146                 simSpeedScale.setIncrement(1);
147                 simSpeedScale.setSelection(0);
148                 simSpeedScale.addListener(SWT.Selection, e ->
149                 {
150                         double speed = Math.pow(SIM_SPEED_SCALE_STEP_FACTOR, simSpeedScale.getSelection() - SIM_SPEED_SCALE_STEPS);
151                         // TODO: disable when debugTarget is not set
152                         debugTarget.setExecutionSpeed(speed);
153                 });
154
155                 simSpeedInput = new DoubleInput(c, SWT.NONE);
156                 controlsToDisableWhenNoMachinePresent.add(simSpeedInput);
157                 simSpeedInput.setPrecision(Preferences.current().getInt("net.mograsim.plugin.core.simspeedprecision"));
158                 simSpeedInput.addChangeListener(speed ->
159                 {
160                         if (speed != 0)
161                                 debugTarget.setExecutionSpeed(speed);
162                         else
163                                 debugTarget.setExecutionSpeed(Math.pow(10, -simSpeedInput.getPrecision()));
164                 });
165
166                 c.layout();
167         }
168
169         private void speedFactorChanged(double speed)
170         {
171                 simSpeedInput.setValue(speed);
172                 int closestScalePos = (int) Math.round(Math.log(speed) / SIM_SPEED_SCALE_STEP_FACTOR_LOG + SIM_SPEED_SCALE_STEPS);
173                 simSpeedScale.setSelection(Math.min(Math.max(closestScalePos, 0), SIM_SPEED_SCALE_STEPS));
174         }
175
176         private void addInstructionPreviewControlWidgets(Composite parent)
177         {
178                 instPreview = new InstructionTable(parent, new DisplaySettings(), getSite().getWorkbenchWindow().getWorkbench().getThemeManager());
179                 instPreview.getTableViewer().getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
180                 contentProvider = new ActiveInstructionPreviewContentProvider(instPreview.getTableViewer());
181                 instPreview.setContentProvider(contentProvider);
182         }
183
184         private void debugContextChanged(Optional<MachineDebugTarget> newTarget)
185         {
186                 // if we didn't find a selected MachineDebugTarget
187                 // call binToDebugTarget even if this.debugTarget==null
188                 bindToDebugTarget(newTarget.orElse(null));
189         }
190
191         private void bindToDebugTarget(MachineDebugTarget debugTarget)
192         {
193                 deregisterMachineDependentListeners();
194                 this.debugTarget = debugTarget;
195
196                 if (canvasParent == null)
197                         // createPartControls has not been called yet
198                         return;
199
200                 double offX;
201                 double offY;
202                 double zoom;
203                 if (canvas != null)
204                 {
205                         offX = canvas.getOffX();
206                         offY = canvas.getOffY();
207                         zoom = canvas.getZoom();
208                         canvas.dispose();
209                 } else
210                 {
211                         offX = 0;
212                         offY = 0;
213                         zoom = -1;
214                 }
215
216                 if (debugTarget != null)
217                 {
218                         noRunningMachineLabel.setVisible(false);
219                         contextDependentControlsParent.setVisible(true);
220                         controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(true));
221
222                         Machine machine = debugTarget.getMachine();
223
224                         canvas = new LogicUICanvas(canvasParent, SWT.NONE, machine.getModel());
225                         canvas.addListener(SWT.MouseDown, e -> canvas.setFocus());
226                         ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(canvas);
227                         userInput.buttonDrag = Preferences.current().getInt("net.mograsim.logic.model.button.drag");
228                         userInput.buttonZoom = Preferences.current().getInt("net.mograsim.logic.model.button.zoom");
229                         userInput.enableUserInput();
230                         if (zoom > 0)
231                         {
232                                 canvas.moveTo(offX, offY, zoom);
233                                 canvas.commitTransform();
234                         }
235
236                         AssignableMicroInstructionMemory mIMemory = machine.getMicroInstructionMemory();
237                         instPreview.bindMicroInstructionMemory(mIMemory);
238                         mIMemory.registerCellModifiedListener(memCellListener);
239
240                         canvasParent.layout();
241
242                         // update preview
243                         contentProvider.setMachine(machine);
244
245                         // initialize executer
246                         debugTarget.addExecutionSpeedListener(executionSpeedListener);
247                         speedFactorChanged(debugTarget.getExecutionSpeed());
248                 } else
249                 {
250                         noRunningMachineLabel.setVisible(true);
251                         contextDependentControlsParent.setVisible(false);
252                         controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(false));
253                         contentProvider.setMachine(null);
254                 }
255         }
256
257         private void deregisterMachineDependentListeners()
258         {
259                 if (debugTarget != null)
260                 {
261                         debugTarget.removeExecutionSpeedListener(executionSpeedListener);
262                         debugTarget.getMachine().getMicroInstructionMemory().deregisterCellModifiedListener(memCellListener);
263                         debugTarget.getMachine().getClock().deregisterObserver(clockObserver);
264                         if (sbseButton != null && !sbseButton.isDisposed())
265                                 sbseButton.setSelection(false);
266                 }
267         }
268
269         @Override
270         public void setFocus()
271         {
272                 if (canvas != null && !canvas.isDisposed())
273                         canvas.setFocus();
274         }
275
276         @Override
277         public void dispose()
278         {
279                 deregisterMachineDependentListeners();
280                 contentProvider.setMachine(null);
281                 DebugUITools.getDebugContextManager().removeDebugContextListener(debugContextListener);
282                 super.dispose();
283         }
284 }