1 package net.mograsim.logic.model;
3 import java.util.ArrayList;
5 import java.util.concurrent.atomic.AtomicBoolean;
6 import java.util.concurrent.atomic.AtomicReference;
7 import java.util.function.Consumer;
9 import org.eclipse.swt.SWT;
10 import org.eclipse.swt.graphics.Color;
11 import org.eclipse.swt.layout.GridData;
12 import org.eclipse.swt.layout.GridLayout;
13 import org.eclipse.swt.widgets.Button;
14 import org.eclipse.swt.widgets.Combo;
15 import org.eclipse.swt.widgets.Composite;
16 import org.eclipse.swt.widgets.Event;
17 import org.eclipse.swt.widgets.Label;
18 import org.eclipse.swt.widgets.Listener;
19 import org.eclipse.swt.widgets.Shell;
20 import org.eclipse.swt.widgets.Text;
22 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
23 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
24 import net.haspamelodica.swt.helper.zoomablecanvas.ZoomableCanvas;
25 import net.mograsim.logic.core.types.Bit;
26 import net.mograsim.logic.core.types.BitVector;
27 import net.mograsim.logic.model.model.LogicModel;
28 import net.mograsim.logic.model.model.components.ModelComponent;
29 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
30 import net.mograsim.logic.model.snippets.highlevelstatehandlers.DefaultHighLevelStateHandler;
31 import net.mograsim.preferences.Preferences;
34 * Simulation visualizer canvas.
36 * @author Daniel Kirschten
38 public class LogicUICanvas extends ZoomableCanvas
40 private final LogicModel model;
42 public LogicUICanvas(Composite parent, int style, LogicModel model)
44 super(parent, style, Preferences.current().getBoolean("net.mograsim.logic.model.improvetext"));
48 LogicUIRenderer renderer = new LogicUIRenderer(model);
49 addZoomedRenderer(gc ->
51 Color background = Preferences.current().getColor("net.mograsim.logic.model.color.background");
52 if (background != null)
53 setBackground(background);// this.setBackground, not gc.setBackground to have the background fill the canvas
54 renderer.render(gc, new Rectangle(-offX / zoom, -offY / zoom, gW / zoom, gH / zoom));
56 model.setRedrawHandler(this::redrawThreadsafe);
58 addListener(SWT.MouseDown, this::mouseDown);
60 if (Preferences.current().getBoolean("net.mograsim.logic.model.debug.openhlsshell"))
61 openDebugSetHighLevelStateShell(model);
64 private void mouseDown(Event e)
68 Point click = canvasToWorldCoords(e.x, e.y);
69 for (ModelComponent component : model.getComponentsByName().values())
70 if (component.getBounds().contains(click) && component.clicked(click.x, click.y))
78 private void openDebugSetHighLevelStateShell(LogicModel model)
80 Shell debugShell = new Shell();
81 debugShell.setLayout(new GridLayout(2, false));
82 new Label(debugShell, SWT.NONE).setText("Target component: ");
83 Combo componentSelector = new Combo(debugShell, SWT.DROP_DOWN | SWT.READ_ONLY);
84 componentSelector.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
85 List<ModelComponent> componentsByItemIndex = new ArrayList<>();
86 List<LogicModel> models = new ArrayList<>();
87 AtomicBoolean recalculateQueued = new AtomicBoolean();
88 AtomicReference<Consumer<? super ModelComponent>> compAdded = new AtomicReference<>();
89 AtomicReference<Consumer<? super ModelComponent>> compRemoved = new AtomicReference<>();
90 compAdded.set(c -> compsChanged(compAdded.get(), compRemoved.get(), c, models, componentsByItemIndex, componentSelector, model,
91 recalculateQueued, true));
92 compRemoved.set(c -> compsChanged(compAdded.get(), compRemoved.get(), c, models, componentsByItemIndex, componentSelector, model,
93 recalculateQueued, false));
94 iterateModelTree(compAdded.get(), compRemoved.get(), model, models, true);
95 debugShell.addListener(SWT.Dispose, e -> models.forEach(m ->
97 m.removeComponentAddedListener(compAdded.get());
98 m.removeComponentRemovedListener(compRemoved.get());
100 queueRecalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model);
101 new Label(debugShell, SWT.NONE).setText("Target state ID: ");
102 Text stateIDText = new Text(debugShell, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
103 stateIDText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
104 new Label(debugShell, SWT.NONE).setText("Value type: ");
105 Composite radioGroup = new Composite(debugShell, SWT.NONE);
106 radioGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
107 GridLayout radioGroupLayout = new GridLayout(2, false);
108 radioGroupLayout.marginHeight = 0;
109 radioGroupLayout.marginWidth = 0;
110 radioGroup.setLayout(radioGroupLayout);
111 Button radioBit = new Button(radioGroup, SWT.RADIO);
112 radioBit.setText("Single bit");
113 Button radioBitVector = new Button(radioGroup, SWT.RADIO);
114 radioBitVector.setText("Bitvector");
115 new Label(debugShell, SWT.NONE).setText("Value string representation: \n(Bit vectors: MSBit...LSBit)");
116 Text valueText = new Text(debugShell, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
117 valueText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
118 Button send = new Button(debugShell, SWT.PUSH);
119 send.setText("Send!");
120 Button get = new Button(debugShell, SWT.PUSH);
122 Text output = new Text(debugShell, SWT.READ_ONLY);
123 output.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
124 Listener sendAction = e ->
128 int componentIndex = componentSelector.getSelectionIndex();
129 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
130 throw new RuntimeException("No component selected");
131 ModelComponent target = componentsByItemIndex.get(componentIndex);
132 String valueString = valueText.getText();
134 if (radioBit.getSelection())
135 value = Bit.parse(valueString);
136 else if (radioBitVector.getSelection())
137 value = BitVector.parse(valueString);
139 throw new RuntimeException("No value type selected");
140 target.setHighLevelState(stateIDText.getText(), value);
141 output.setText("Success!");
145 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
148 Listener getAction = e ->
152 if (componentSelector.getSelectionIndex() >= componentsByItemIndex.size())
153 throw new RuntimeException("No valid component selected");
154 output.setText("Success! Value: \r\n"
155 + componentsByItemIndex.get(componentSelector.getSelectionIndex()).getHighLevelState(stateIDText.getText()));
159 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
162 send.addListener(SWT.Selection, sendAction);
163 valueText.addListener(SWT.DefaultSelection, sendAction);
164 get.addListener(SWT.Selection, getAction);
165 stateIDText.addListener(SWT.DefaultSelection, getAction);
169 private void compsChanged(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved, ModelComponent c,
170 List<LogicModel> models, List<ModelComponent> componentsByItemIndex, Combo componentSelector, LogicModel model,
171 AtomicBoolean recalculateQueued, boolean add)
173 iterateSubmodelTree(compAdded, compRemoved, c, models, add);
174 queueRecalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model);
177 private void iterateSubmodelTree(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved,
178 ModelComponent c, List<LogicModel> models, boolean add)
180 if (c instanceof SubmodelComponent)
181 iterateModelTree(compAdded, compRemoved, ((SubmodelComponent) c).submodel, models, add);
184 private void iterateModelTree(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved,
185 LogicModel model, List<LogicModel> models, boolean add)
187 if (add ^ models.contains(model))
192 model.addComponentAddedListener(compAdded);
193 model.addComponentRemovedListener(compRemoved);
196 models.remove(model);
197 model.removeComponentAddedListener(compAdded);
198 model.removeComponentRemovedListener(compRemoved);
200 for (ModelComponent c : model.getComponentsByName().values())
201 iterateSubmodelTree(compAdded, compRemoved, c, models, add);
205 private void queueRecalculateComponentSelector(AtomicBoolean recalculateQueued, List<ModelComponent> componentsByItemIndex,
206 Combo componentSelector, LogicModel model)
208 if (recalculateQueued.compareAndSet(false, true))
209 getDisplay().asyncExec(() -> recalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model));
212 private void recalculateComponentSelector(AtomicBoolean recalculateQueued, List<ModelComponent> componentsByItemIndex,
213 Combo componentSelector, LogicModel model)
215 recalculateQueued.set(false);
216 componentsByItemIndex.clear();
217 componentSelector.setItems();
218 addComponentSelectorItems(componentsByItemIndex, "", componentSelector, model,
219 Preferences.current().getInt("net.mograsim.logic.model.debug.hlsshelldepth") - 1);
222 private void addComponentSelectorItems(List<ModelComponent> componentsByItemIndex, String base, Combo componentSelector,
223 LogicModel model, int depth)
225 model.getComponentsByName().values().stream().sorted((c1, c2) -> c1.getName().compareTo(c2.getName())).forEach(c ->
227 if (!(c.getHighLevelStateHandler() instanceof DefaultHighLevelStateHandler))
229 String item = base + c.getName();
230 componentsByItemIndex.add(c);
231 componentSelector.add(item);
232 // this causes negative numbers to result in infinite depth
233 if (depth != 0 && c instanceof SubmodelComponent)
234 addComponentSelectorItems(componentsByItemIndex, item + " -> ", componentSelector, ((SubmodelComponent) c).submodel,