1 package net.mograsim.logic.model;
3 import static net.mograsim.logic.model.preferences.RenderPreferences.ACTION_BUTTON;
4 import static net.mograsim.logic.model.preferences.RenderPreferences.BACKGROUND_COLOR;
5 import static net.mograsim.logic.model.preferences.RenderPreferences.DEBUG_HLSSHELL_DEPTH;
6 import static net.mograsim.logic.model.preferences.RenderPreferences.DEBUG_OPEN_HLSSHELL;
7 import static net.mograsim.logic.model.preferences.RenderPreferences.IMPROVE_SCALING;
8 import static net.mograsim.logic.model.preferences.RenderPreferences.LINE_DASH_IMPROVEMENT_FACTOR;
10 import java.util.ArrayList;
11 import java.util.HashMap;
12 import java.util.List;
14 import java.util.concurrent.atomic.AtomicBoolean;
15 import java.util.function.Consumer;
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.graphics.Color;
19 import org.eclipse.swt.layout.GridData;
20 import org.eclipse.swt.layout.GridLayout;
21 import org.eclipse.swt.widgets.Button;
22 import org.eclipse.swt.widgets.Combo;
23 import org.eclipse.swt.widgets.Composite;
24 import org.eclipse.swt.widgets.Event;
25 import org.eclipse.swt.widgets.Label;
26 import org.eclipse.swt.widgets.Listener;
27 import org.eclipse.swt.widgets.Shell;
28 import org.eclipse.swt.widgets.Text;
30 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
31 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
32 import net.haspamelodica.swt.helper.zoomablecanvas.ZoomableCanvas;
33 import net.mograsim.logic.core.types.Bit;
34 import net.mograsim.logic.core.types.BitVector;
35 import net.mograsim.logic.model.model.LogicModel;
36 import net.mograsim.logic.model.model.components.ModelComponent;
37 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
38 import net.mograsim.logic.model.preferences.RenderPreferences;
39 import net.mograsim.logic.model.snippets.highlevelstatehandlers.DefaultHighLevelStateHandler;
42 * Simulation visualizer canvas.
44 * @author Daniel Kirschten
46 public class LogicUICanvas extends ZoomableCanvas
48 protected final LogicModel model;
49 protected final RenderPreferences renderPrefs;
51 public LogicUICanvas(Composite parent, int style, LogicModel model, RenderPreferences renderPrefs)
53 // TODO add a listener
54 super(parent, style, renderPrefs.getBoolean(IMPROVE_SCALING), (float) renderPrefs.getDouble(LINE_DASH_IMPROVEMENT_FACTOR));
56 this.renderPrefs = renderPrefs;
59 // TODO add listeners for the render prefs
61 Color background = renderPrefs.getColor(BACKGROUND_COLOR);
62 if (background != null)
63 setBackground(background);
65 LogicUIRenderer renderer = new LogicUIRenderer(model);
66 addZoomedRenderer(gc -> renderer.render(gc, renderPrefs, new Rectangle(-offX / zoom, -offY / zoom, gW / zoom, gH / zoom)));
67 model.setRedrawHandler(() ->
73 addListener(SWT.MouseDown, this::mouseDown);
75 if (renderPrefs.getBoolean(DEBUG_OPEN_HLSSHELL))
76 openDebugSetHighLevelStateShell(model, renderPrefs.getInt(DEBUG_HLSSHELL_DEPTH) - 1);
79 private void mouseDown(Event e)
81 if (e.button == renderPrefs.getInt(ACTION_BUTTON))
83 Point click = canvasToWorldCoords(e.x, e.y);
84 for (ModelComponent component : model.getComponentsByName().values())
85 if (component.getBounds().contains(click) && component.clicked(click.x, click.y))
93 private void openDebugSetHighLevelStateShell(LogicModel model, int depth)
95 Shell debugShell = new Shell();
96 debugShell.setLayout(new GridLayout(2, false));
97 new Label(debugShell, SWT.NONE).setText("Target component: ");
98 Combo componentSelector = new Combo(debugShell, SWT.DROP_DOWN | SWT.READ_ONLY);
99 componentSelector.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
100 List<ModelComponent> componentsByItemIndex = new ArrayList<>();
101 List<LogicModel> models = new ArrayList<>();
102 AtomicBoolean recalculateQueued = new AtomicBoolean();
103 @SuppressWarnings("unchecked")
104 Consumer<? super ModelComponent>[] compAdded = new Consumer[1];
105 @SuppressWarnings("unchecked")
106 Consumer<? super ModelComponent>[] compRemoved = new Consumer[1];
107 compAdded[0] = c -> compsChanged(compAdded[0], compRemoved[0], c, models, componentsByItemIndex, componentSelector, model,
108 recalculateQueued, depth, true);
109 compRemoved[0] = c -> compsChanged(compAdded[0], compRemoved[0], c, models, componentsByItemIndex, componentSelector, model,
110 recalculateQueued, depth, false);
111 iterateModelTree(compAdded[0], compRemoved[0], model, models, true);
112 debugShell.addListener(SWT.Dispose, e -> models.forEach(m ->
114 m.removeComponentAddedListener(compAdded[0]);
115 m.removeComponentRemovedListener(compRemoved[0]);
117 queueRecalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model, depth);
118 new Label(debugShell, SWT.NONE).setText("Target state ID: ");
119 Text stateIDText = new Text(debugShell, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
120 stateIDText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
121 new Label(debugShell, SWT.NONE).setText("Value type: ");
122 Composite radioGroup = new Composite(debugShell, SWT.NONE);
123 radioGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
124 GridLayout radioGroupLayout = new GridLayout(2, false);
125 radioGroupLayout.marginHeight = 0;
126 radioGroupLayout.marginWidth = 0;
127 radioGroup.setLayout(radioGroupLayout);
128 Button radioBit = new Button(radioGroup, SWT.RADIO);
129 radioBit.setText("Single bit");
130 Button radioBitVector = new Button(radioGroup, SWT.RADIO);
131 radioBitVector.setText("Bitvector");
132 new Label(debugShell, SWT.NONE).setText("Value string representation: \n(Bit vectors: MSBit...LSBit)");
133 Text valueText = new Text(debugShell, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
134 valueText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
135 Button send = new Button(debugShell, SWT.PUSH);
136 send.setText("Send!");
137 Button addListener = new Button(debugShell, SWT.PUSH);
138 addListener.setText("Add sysout listener");
139 Button get = new Button(debugShell, SWT.PUSH);
141 Button removeListener = new Button(debugShell, SWT.PUSH);
142 removeListener.setText("Remove sysout listener");
143 Text output = new Text(debugShell, SWT.READ_ONLY);
144 output.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
145 Listener sendAction = e ->
149 int componentIndex = componentSelector.getSelectionIndex();
150 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
151 throw new RuntimeException("No component selected");
152 ModelComponent target = componentsByItemIndex.get(componentIndex);
153 String valueString = valueText.getText();
154 String stateID = stateIDText.getText();
156 if (radioBit.getSelection())
157 value = Bit.parse(valueString);
158 else if (radioBitVector.getSelection())
160 Object hls = target.getHighLevelState(stateID);
162 if (hls instanceof Bit)
164 else if (hls instanceof BitVector)
165 width = ((BitVector) hls).length();
168 value = BitVectorFormatter.parseUserBitVector(valueString, width);
170 throw new RuntimeException("No value type selected");
171 target.setHighLevelState(stateID, value);
172 output.setText("Success!");
176 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
179 Listener getAction = e ->
183 int componentIndex = componentSelector.getSelectionIndex();
184 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
185 throw new RuntimeException("No component selected");
186 output.setText("Success! Value: \r\n"
187 + componentsByItemIndex.get(componentSelector.getSelectionIndex()).getHighLevelState(stateIDText.getText()));
191 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
194 send.addListener(SWT.Selection, sendAction);
195 valueText.addListener(SWT.DefaultSelection, sendAction);
196 get.addListener(SWT.Selection, getAction);
197 stateIDText.addListener(SWT.DefaultSelection, getAction);
198 Map<ModelComponent, Map<String, Consumer<Object>>> sysoutListenersPerHLSPerTarget = new HashMap<>();
199 addListener.addListener(SWT.Selection, e ->
203 int componentIndex = componentSelector.getSelectionIndex();
204 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
205 throw new RuntimeException("No component selected");
206 ModelComponent target = componentsByItemIndex.get(componentIndex);
207 Map<String, Consumer<Object>> sysoutListenersPerHLS = sysoutListenersPerHLSPerTarget.computeIfAbsent(target,
208 k -> new HashMap<>());
209 String stateIDString = stateIDText.getText();
210 if (sysoutListenersPerHLS.containsKey(stateIDString))
211 throw new RuntimeException("Listener already registered");
212 Consumer<Object> sysoutListener = v -> System.out.println(stateIDString + ": " + v);
213 target.addHighLevelStateListener(stateIDString, sysoutListener);
214 sysoutListenersPerHLS.put(stateIDString, sysoutListener);
215 output.setText("Success!");
219 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
222 removeListener.addListener(SWT.Selection, e ->
226 int componentIndex = componentSelector.getSelectionIndex();
227 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
228 throw new RuntimeException("No component selected");
229 ModelComponent target = componentsByItemIndex.get(componentIndex);
230 Map<String, Consumer<Object>> sysoutListenersPerHLS = sysoutListenersPerHLSPerTarget.get(target);
231 if (sysoutListenersPerHLS == null)
232 throw new RuntimeException("Listener not registered");
233 String stateIDString = stateIDText.getText();
234 Consumer<Object> sysoutListener = sysoutListenersPerHLS.remove(stateIDString);
235 if (sysoutListener == null)
236 throw new RuntimeException("Listener not registered");
237 target.removeHighLevelStateListener(stateIDString, sysoutListener);
238 output.setText("Success!");
242 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
246 addDisposeListener(e -> debugShell.dispose());
249 private void compsChanged(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved, ModelComponent c,
250 List<LogicModel> models, List<ModelComponent> componentsByItemIndex, Combo componentSelector, LogicModel model,
251 AtomicBoolean recalculateQueued, int depth, boolean add)
253 iterateSubmodelTree(compAdded, compRemoved, c, models, add);
254 queueRecalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model, depth);
257 private void iterateSubmodelTree(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved,
258 ModelComponent c, List<LogicModel> models, boolean add)
260 if (c instanceof SubmodelComponent)
261 iterateModelTree(compAdded, compRemoved, ((SubmodelComponent) c).submodel, models, add);
264 private void iterateModelTree(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved,
265 LogicModel model, List<LogicModel> models, boolean add)
267 if (add ^ models.contains(model))
272 model.addComponentAddedListener(compAdded);
273 model.addComponentRemovedListener(compRemoved);
276 models.remove(model);
277 model.removeComponentAddedListener(compAdded);
278 model.removeComponentRemovedListener(compRemoved);
280 for (ModelComponent c : model.getComponentsByName().values())
281 iterateSubmodelTree(compAdded, compRemoved, c, models, add);
285 private void queueRecalculateComponentSelector(AtomicBoolean recalculateQueued, List<ModelComponent> componentsByItemIndex,
286 Combo componentSelector, LogicModel model, int depth)
288 if (recalculateQueued.compareAndSet(false, true))
289 getDisplay().asyncExec(
290 () -> recalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model, depth));
293 private void recalculateComponentSelector(AtomicBoolean recalculateQueued, List<ModelComponent> componentsByItemIndex,
294 Combo componentSelector, LogicModel model, int depth)
296 recalculateQueued.set(false);
297 componentsByItemIndex.clear();
298 componentSelector.setItems();
299 addComponentSelectorItems(componentsByItemIndex, "", componentSelector, model, depth);
302 private void addComponentSelectorItems(List<ModelComponent> componentsByItemIndex, String base, Combo componentSelector,
303 LogicModel model, int depth)
305 model.getComponentsByName().values().stream().sorted((c1, c2) -> c1.getName().compareTo(c2.getName())).forEach(c ->
307 if (!(c.getHighLevelStateHandler() instanceof DefaultHighLevelStateHandler))
309 String item = base + c.getName();
310 componentsByItemIndex.add(c);
311 componentSelector.add(item);
312 // this causes negative numbers to result in infinite depth
313 if (depth != 0 && c instanceof SubmodelComponent)
314 addComponentSelectorItems(componentsByItemIndex, item + " -> ", componentSelector, ((SubmodelComponent) c).submodel,