Merge branch 'development' of
authorFabian Stemmler <stemmler@in.tum.de>
Wed, 29 May 2019 10:42:50 +0000 (12:42 +0200)
committerFabian Stemmler <stemmler@in.tum.de>
Wed, 29 May 2019 10:42:50 +0000 (12:42 +0200)
https://gitlab.lrz.de/lrr-tum/students/eragp-misim-2019 into development

# Conflicts:
# LogicUI/oldsrc/RSLatchGUIExample.java
# LogicUI/src/era/mi/gui/LogicUIStandalone.java
# LogicUI/src/era/mi/gui/components/GUIManualSwitch.java
# SampleERCP/src/sampleercp/parts/LogicUIPart.java

34 files changed:
LogicUI/oldsrc/GUIMerger.java [new file with mode: 0644]
LogicUI/oldsrc/GUIMux.java [new file with mode: 0644]
LogicUI/oldsrc/GUISplitter.java [new file with mode: 0644]
LogicUI/oldsrc/RSLatchGUIExample.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/ColorHelper.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/LogicUICanvas.java
LogicUI/src/era/mi/gui/LogicUIStandalone.java
LogicUI/src/era/mi/gui/components/BasicGUIComponent.java [deleted file]
LogicUI/src/era/mi/gui/components/GUIAndGate.java [deleted file]
LogicUI/src/era/mi/gui/components/GUIManualSwitch.java
LogicUI/src/era/mi/gui/components/GUIMerger.java [deleted file]
LogicUI/src/era/mi/gui/components/GUIMux.java [deleted file]
LogicUI/src/era/mi/gui/components/GUINotGate.java [deleted file]
LogicUI/src/era/mi/gui/components/GUIOrGate.java [deleted file]
LogicUI/src/era/mi/gui/components/GUISplitter.java [deleted file]
LogicUI/src/era/mi/gui/examples/Playground.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/examples/RSLatchGUIExample.java [deleted file]
LogicUI/src/era/mi/gui/model/ViewModel.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/components/GUIAndGate.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/components/GUIComponent.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/components/GUINotGate.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/components/GUIOrGate.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/components/RectangularShapedGUIGate.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/wires/GUIWire.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/wires/MovablePin.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/wires/Pin.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java [new file with mode: 0644]
LogicUI/src/era/mi/gui/wires/GUIWire.java [deleted file]
LogicUI/src/era/mi/gui/wires/WireConnectionPoint.java [deleted file]
SampleERCP/src/sampleercp/parts/LogicUIPart.java
era.mi/src/era/mi/logic/types/BitVectorFormatter.java [new file with mode: 0644]
era.mi/src/era/mi/logic/types/ColorDefinition.java [new file with mode: 0644]

diff --git a/LogicUI/oldsrc/GUIMerger.java b/LogicUI/oldsrc/GUIMerger.java
new file mode 100644 (file)
index 0000000..64fd0aa
--- /dev/null
@@ -0,0 +1,79 @@
+package era.mi.gui.components;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import era.mi.logic.components.Merger;
+import era.mi.logic.wires.Wire.ReadEnd;
+import era.mi.logic.wires.Wire.ReadWriteEnd;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class GUIMerger extends Merger implements GUIComponent
+{
+       private final int inputCount;
+       private final double height;
+       private final List<ReadEnd> connectedWireEnds;
+       private final List<Point> WireEndConnectionPoints;
+
+       public GUIMerger(ReadWriteEnd union, ReadEnd... inputs)
+       {
+               super(union, inputs);
+
+               List<ReadEnd> connectedWireEndsModifiable = new ArrayList<>();
+               List<Point> WireEndConnectionPointsModifiable = new ArrayList<>();
+
+               this.inputCount = inputs.length;
+               this.height = (inputCount - 1) * 10;
+
+               {
+                       connectedWireEndsModifiable.addAll(Arrays.asList(inputs));
+                       double inputHeight = 0;
+                       for (int i = 0; i < inputCount; i++, inputHeight += 10)
+                               WireEndConnectionPointsModifiable.add(new Point(0, inputHeight));
+               }
+
+               connectedWireEndsModifiable.add(union);
+               WireEndConnectionPointsModifiable.add(new Point(20, height / 2));
+
+               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);
+               this.WireEndConnectionPoints = Collections.unmodifiableList(WireEndConnectionPointsModifiable);
+       }
+
+       @Override
+       public Rectangle getBounds()
+       {
+               return new Rectangle(0, 0, 20, height);
+       }
+
+       @Override
+       public void render(GeneralGC gc)
+       {
+               double inputHeight = 0;
+               for (int i = 0; i < inputCount; i++, inputHeight += 10)
+                       gc.drawLine(0, inputHeight, 10, inputHeight);
+               gc.drawLine(10, 0, 10, height);
+               gc.drawLine(10, height / 2, 20, height / 2);
+       }
+
+       @Override
+       public int getConnectedWireEndsCount()
+       {
+               return connectedWireEnds.size();
+       }
+
+       @Override
+       public ReadEnd getConnectedWireEnd(int connectionIndex)
+       {
+               return connectedWireEnds.get(connectionIndex);
+       }
+
+       @Override
+       public Point getWireEndConnectionPoint(int connectionI)
+       {
+               return WireEndConnectionPoints.get(connectionI);
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/oldsrc/GUIMux.java b/LogicUI/oldsrc/GUIMux.java
new file mode 100644 (file)
index 0000000..d004d90
--- /dev/null
@@ -0,0 +1,80 @@
+package era.mi.gui.components;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import era.mi.logic.components.Mux;
+import era.mi.logic.wires.Wire.ReadEnd;
+import era.mi.logic.wires.Wire.ReadWriteEnd;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class GUIMux extends Mux implements GUIComponent
+{
+       private final double height;
+       private final List<ReadEnd> connectedWireEnds;
+       private final List<Point> WireEndConnectionPoints;
+
+       public GUIMux(int processTime, ReadWriteEnd out, ReadEnd select, ReadEnd... inputs)
+       {
+               super(processTime, out, select, inputs);
+
+               double height = inputs.length * 5;
+               if (height < 10)
+                       height = 10;
+               this.height = height;
+
+               List<ReadEnd> connectedWireEndsModifiable = new ArrayList<>();
+               List<Point> WireEndConnectionPointsModifiable = new ArrayList<>();
+
+               connectedWireEndsModifiable.add(out);
+               WireEndConnectionPointsModifiable.add(new Point(20, 10 + height / 2));
+
+               connectedWireEndsModifiable.add(select);
+               WireEndConnectionPointsModifiable.add(new Point(10, 5));
+
+               {
+                       connectedWireEndsModifiable.addAll(Arrays.asList(inputs));
+                       double inputHeightIncrement = (height + 20) / inputs.length;
+                       double inputHeight = inputHeightIncrement / 2;
+                       for (int i = 0; i < inputs.length; i++, inputHeight += inputHeightIncrement)
+                               WireEndConnectionPointsModifiable.add(new Point(0, inputHeight));
+               }
+
+               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);
+               this.WireEndConnectionPoints = Collections.unmodifiableList(WireEndConnectionPointsModifiable);
+       }
+
+       @Override
+       public Rectangle getBounds()
+       {
+               return new Rectangle(0, 0, 20, height + 20);
+       }
+
+       @Override
+       public void render(GeneralGC gc)
+       {
+               gc.drawPolygon(new double[] { 0, 0, 20, 10, 20, height + 10, 0, height + 20 });
+       }
+
+       @Override
+       public int getConnectedWireEndsCount()
+       {
+               return connectedWireEnds.size();
+       }
+
+       @Override
+       public ReadEnd getConnectedWireEnd(int connectionIndex)
+       {
+               return connectedWireEnds.get(connectionIndex);
+       }
+
+       @Override
+       public Point getWireEndConnectionPoint(int connectionI)
+       {
+               return WireEndConnectionPoints.get(connectionI);
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/oldsrc/GUISplitter.java b/LogicUI/oldsrc/GUISplitter.java
new file mode 100644 (file)
index 0000000..1bff424
--- /dev/null
@@ -0,0 +1,72 @@
+package era.mi.gui.components;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import era.mi.gui.ViewModel;
+import era.mi.logic.components.Splitter;
+import era.mi.logic.wires.Wire.ReadEnd;
+import era.mi.logic.wires.Wire.ReadWriteEnd;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class GUISplitter extends GUIComponent
+{
+       public GUISplitter(ViewModel model)
+       {
+               super(model);
+
+               this.outputCount = outputs.length;
+               this.height = (outputCount - 1) * 10;
+
+               connectedWireEndsModifiable.add(input);
+               WireEndConnectionPointsModifiable.add(new Point(0, height / 2));
+
+               {
+                       connectedWireEndsModifiable.addAll(Arrays.asList(outputs));
+                       double outputHeight = 0;
+                       for (int i = 0; i < outputCount; i++, outputHeight += 10)
+                               WireEndConnectionPointsModifiable.add(new Point(20, outputHeight));
+               }
+
+               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);
+               this.WireEndConnectionPoints = Collections.unmodifiableList(WireEndConnectionPointsModifiable);
+       }
+
+       @Override
+       public Rectangle getBounds()
+       {
+               return new Rectangle(0, 0, 20, height);
+       }
+
+       @Override
+       public void render(GeneralGC gc)
+       {
+               gc.drawLine(0, height / 2, 10, height / 2);
+               gc.drawLine(10, 0, 10, height);
+               double outputHeight = 0;
+               for (int i = 0; i < outputCount; i++, outputHeight += 10)
+                       gc.drawLine(10, outputHeight, 20, outputHeight);
+       }
+
+       @Override
+       public int getConnectedWireEndsCount()
+       {
+               return connectedWireEnds.size();
+       }
+
+       @Override
+       public ReadEnd getConnectedWireEnd(int connectionIndex)
+       {
+               return connectedWireEnds.get(connectionIndex);
+       }
+
+       @Override
+       public Point getWireEndConnectionPoint(int connectionI)
+       {
+               return WireEndConnectionPoints.get(connectionI);
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/oldsrc/RSLatchGUIExample.java b/LogicUI/oldsrc/RSLatchGUIExample.java
new file mode 100644 (file)
index 0000000..413525b
--- /dev/null
@@ -0,0 +1,62 @@
+package era.mi.gui.examples;\r
+\r
+import era.mi.gui.LogicUICanvas;\r
+import era.mi.gui.LogicUIStandalone;\r
+import era.mi.gui.components.GUIManualSwitch;\r
+import era.mi.gui.components.GUINotGate;\r
+import era.mi.gui.components.GUIOrGateOld;\r
+import era.mi.gui.wires.WireConnectionPoint;\r
+import era.mi.logic.timeline.Timeline;\r
+import era.mi.logic.wires.Wire;\r
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
+\r
+public class RSLatchGUIExample\r
+{\r
+       private static final int WIRE_DELAY = 10;\r
+       private static final int OR_DELAY = 50;\r
+       private static final int NOT_DELAY = 50;\r
+\r
+       public static void main(String[] args)\r
+       {\r
+               Timeline t = new Timeline(11);\r
+               t.setTimeFunction(() -> System.currentTimeMillis()); // real time simulation\r
+               LogicUIStandalone ui = new LogicUIStandalone(t);\r
+               addComponentsAndWires(ui.getLogicUICanvas(), t);\r
+               ui.run();\r
+       }\r
+\r
+       public static void addComponentsAndWires(LogicUICanvas ui, Timeline t)\r
+       {\r
+               Wire r = new Wire(t, 1, WIRE_DELAY);\r
+               Wire s = new Wire(t, 1, WIRE_DELAY);\r
+               Wire t2 = new Wire(t, 1, WIRE_DELAY);\r
+               Wire t1 = new Wire(t, 1, WIRE_DELAY);\r
+               Wire q = new Wire(t, 1, WIRE_DELAY);\r
+               Wire nq = new Wire(t, 1, WIRE_DELAY);\r
+\r
+               GUIManualSwitch rIn = ui.addComponent(new GUIManualSwitch(t, r.createReadWriteEnd()), 100, 100);\r
+               GUIManualSwitch sIn = ui.addComponent(new GUIManualSwitch(t, s.createReadWriteEnd()), 100, 200);\r
+               GUIOrGateOld or1 = ui.addComponent(new GUIOrGateOld(t, OR_DELAY, t1.createReadWriteEnd(), r.createReadOnlyEnd(), nq.createReadOnlyEnd()),\r
+                               160, 102.5);\r
+               GUIOrGateOld or2 = ui.addComponent(new GUIOrGateOld(t, OR_DELAY, t2.createReadWriteEnd(), q.createReadOnlyEnd(), s.createReadOnlyEnd()),\r
+                               160, 192.5);\r
+               GUINotGate not1 = ui.addComponent(new GUINotGate(t, NOT_DELAY, t1.createReadOnlyEnd(), q.createReadWriteEnd()), 200, 107.5);\r
+               GUINotGate not2 = ui.addComponent(new GUINotGate(t, NOT_DELAY, t2.createReadOnlyEnd(), nq.createReadWriteEnd()), 200, 197.5);\r
+\r
+               WireConnectionPoint p1 = ui.addComponent(new WireConnectionPoint(q, 3), 250, 112.5);\r
+               WireConnectionPoint p2 = ui.addComponent(new WireConnectionPoint(nq, 3), 250, 202.5);\r
+               WireConnectionPoint o1 = ui.addComponent(new WireConnectionPoint(q, 1), 270, 112.5);\r
+               WireConnectionPoint o2 = ui.addComponent(new WireConnectionPoint(nq, 1), 270, 202.5);\r
+\r
+               ui.addWire(rIn, 0, or1, 0);\r
+               ui.addWire(sIn, 0, or2, 1);\r
+               ui.addWire(or1, 2, not1, 0);\r
+               ui.addWire(or2, 2, not2, 0);\r
+               ui.addWire(not1, 1, p1, 0);\r
+               ui.addWire(not2, 1, p2, 0);\r
+               ui.addWire(p1, 1, or2, 0, new Point(250, 130), new Point(140, 185), new Point(140, 197.5));\r
+               ui.addWire(p2, 1, or1, 1, new Point(250, 185), new Point(140, 130), new Point(140, 117.5));\r
+               ui.addWire(p1, 2, o1, 0);\r
+               ui.addWire(p2, 2, o2, 0);\r
+       }\r
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/ColorHelper.java b/LogicUI/src/era/mi/gui/ColorHelper.java
new file mode 100644 (file)
index 0000000..ff6a495
--- /dev/null
@@ -0,0 +1,91 @@
+package era.mi.gui;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Device;
+
+import era.mi.logic.types.ColorDefinition;
+import era.mi.logic.types.ColorDefinition.BuiltInColor;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+
+//TODO replace with a proper ColorManager
+public class ColorHelper
+{
+       public static void executeWithDifferentForeground(GeneralGC gc, ColorDefinition col, Runnable exec)
+       {
+               executeWithDifferentColor(gc.getDevice(), col, gc::getForeground, gc::setForeground, exec);
+       }
+
+       public static void executeWithDifferentBackground(GeneralGC gc, ColorDefinition col, Runnable exec)
+       {
+               executeWithDifferentColor(gc.getDevice(), col, gc::getBackground, gc::setBackground, exec);
+       }
+
+       private static void executeWithDifferentColor(Device device, ColorDefinition col, Supplier<Color> getColor, Consumer<Color> setColor,
+                       Runnable exec)
+       {
+               Color oldColor = getColor.get();
+               boolean isNoSystemColor = col.builtInColor == null;
+               Color newColor;
+               if (isNoSystemColor)
+                       newColor = new Color(device, col.r, col.g, col.b);
+               else
+                       newColor = device.getSystemColor(ColorHelper.toSWTColorConstant(col.builtInColor));
+               setColor.accept(newColor);
+
+               exec.run();
+
+               setColor.accept(oldColor);
+               if (isNoSystemColor)
+                       newColor.dispose();
+       }
+
+       public static int toSWTColorConstant(BuiltInColor col)
+       {
+               switch (col)
+               {
+               case COLOR_BLACK:
+                       return SWT.COLOR_BLACK;
+               case COLOR_BLUE:
+                       return SWT.COLOR_BLUE;
+               case COLOR_CYAN:
+                       return SWT.COLOR_CYAN;
+               case COLOR_DARK_BLUE:
+                       return SWT.COLOR_DARK_BLUE;
+               case COLOR_DARK_CYAN:
+                       return SWT.COLOR_DARK_CYAN;
+               case COLOR_DARK_GRAY:
+                       return SWT.COLOR_DARK_GRAY;
+               case COLOR_DARK_GREEN:
+                       return SWT.COLOR_DARK_GREEN;
+               case COLOR_DARK_MAGENTA:
+                       return SWT.COLOR_DARK_MAGENTA;
+               case COLOR_DARK_RED:
+                       return SWT.COLOR_DARK_RED;
+               case COLOR_DARK_YELLOW:
+                       return SWT.COLOR_DARK_YELLOW;
+               case COLOR_GRAY:
+                       return SWT.COLOR_GRAY;
+               case COLOR_GREEN:
+                       return SWT.COLOR_GREEN;
+               case COLOR_MAGENTA:
+                       return SWT.COLOR_MAGENTA;
+               case COLOR_RED:
+                       return SWT.COLOR_RED;
+               case COLOR_WHITE:
+                       return SWT.COLOR_WHITE;
+               case COLOR_YELLOW:
+                       return SWT.COLOR_YELLOW;
+               default:
+                       throw new IllegalArgumentException("Unknown enum constant: " + col);
+               }
+       }
+
+       private ColorHelper()
+       {
+               throw new UnsupportedOperationException("No instances of ColorHelper");
+       }
+}
\ No newline at end of file
index dadbae0..2fbb45d 100644 (file)
@@ -1,19 +1,17 @@
 package era.mi.gui;\r
 \r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Map;\r
-import java.util.Set;\r
+import java.util.function.Consumer;\r
 \r
 import org.eclipse.swt.SWT;\r
 import org.eclipse.swt.widgets.Composite;\r
 import org.eclipse.swt.widgets.Event;\r
 \r
-import era.mi.gui.components.BasicGUIComponent;\r
-import era.mi.gui.wires.GUIWire;\r
+import era.mi.gui.model.ViewModel;\r
+import era.mi.gui.model.components.GUIComponent;\r
+import era.mi.gui.model.wires.Pin;\r
 import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.gcs.TranslatedGC;\r
 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
 import net.haspamelodica.swt.helper.zoomablecanvas.ZoomableCanvas;\r
 \r
 /**\r
@@ -23,53 +21,70 @@ import net.haspamelodica.swt.helper.zoomablecanvas.ZoomableCanvas;
  */\r
 public class LogicUICanvas extends ZoomableCanvas\r
 {\r
-       private final Set<BasicGUIComponent> components;\r
-       private final Map<BasicGUIComponent, Point> componentPositions;\r
-       private final Set<GUIWire> wires;\r
+       private final ViewModel model;\r
 \r
-       public LogicUICanvas(Composite parent, int style)\r
+       public LogicUICanvas(Composite parent, int style, ViewModel model)\r
        {\r
                super(parent, style);\r
 \r
-               components = new HashSet<>();\r
-               componentPositions = new HashMap<>();\r
-               wires = new HashSet<>();\r
+               this.model = model;\r
 \r
-               addZoomedRenderer(gc -> components.forEach(c -> drawComponent(gc, c)));\r
-               addZoomedRenderer(gc -> wires.forEach(w -> w.render(gc)));\r
-               addListener(SWT.MouseDown, this::mouseDown);\r
-       }\r
-\r
-       /**\r
-        * Add a component to be drawn. Returns the given component for convenience.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public <C extends BasicGUIComponent> C addComponent(C component, double x, double y)\r
-       {\r
-               components.add(component);\r
-               componentPositions.put(component, new Point(x, y));\r
-               return component;\r
-       }\r
+               Consumer<Object> redrawConsumer = o -> redrawThreadsafe();\r
+               Consumer<Pin> pinAddedListener = p ->\r
+               {\r
+                       p.addPinMovedListener(redrawConsumer);\r
+                       redrawThreadsafe();\r
+               };\r
+               Consumer<Pin> pinRemovedListener = p ->\r
+               {\r
+                       p.removePinMovedListener(redrawConsumer);\r
+                       redrawThreadsafe();\r
+               };\r
+               model.addComponentAddedListener(c ->\r
+               {\r
+                       c.addComponentChangedListener(redrawConsumer);\r
+                       c.addComponentMovedListener(redrawConsumer);\r
+                       c.addPinAddedListener(pinAddedListener);\r
+                       c.addPinRemovedListener(pinRemovedListener);\r
+                       redrawThreadsafe();\r
+               });\r
+               model.addComponentRemovedListener(c ->\r
+               {\r
+                       c.removeComponentChangedListener(redrawConsumer);\r
+                       c.removeComponentMovedListener(redrawConsumer);\r
+                       c.removePinAddedListener(pinAddedListener);\r
+                       c.removePinRemovedListener(pinRemovedListener);\r
+                       redrawThreadsafe();\r
+               });\r
+               model.addWireAddedListener(w ->\r
+               {\r
+                       w.addWireChangedListener(redrawConsumer);\r
+                       redrawThreadsafe();\r
+               });\r
+               model.addWireRemovedListener(w ->\r
+               {\r
+                       w.removeWireChangedListener(redrawConsumer);\r
+                       redrawThreadsafe();\r
+               });\r
 \r
-       /**\r
-        * Add a graphical wire between the given connection points of the given components. The given components have to be added and the given\r
-        * connection points have to be connected logically first.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public void addWire(BasicGUIComponent component1, int component1ConnectionIndex, BasicGUIComponent component2,\r
-                       int component2ConnectionIndex, Point... path)\r
-       {\r
-               wires.add(new GUIWire(this::redrawThreadsafe, component1, component1ConnectionIndex, componentPositions.get(component1), component2,\r
-                               component2ConnectionIndex, componentPositions.get(component2), path));\r
+               addZoomedRenderer(gc ->\r
+               {\r
+                       Rectangle visibleRegion = new Rectangle(offX, offY, gW / zoom, gH / zoom);\r
+                       model.getComponents().forEach(c -> drawComponent(gc, c, visibleRegion));\r
+               });\r
+               addZoomedRenderer(gc -> model.getWires().forEach(w -> w.render(gc)));\r
+               addListener(SWT.MouseDown, this::mouseDown);\r
        }\r
 \r
-       private void drawComponent(GeneralGC gc, BasicGUIComponent component)\r
+       private void drawComponent(GeneralGC gc, GUIComponent component, Rectangle visibleRegion)\r
        {\r
-               TranslatedGC tgc = new TranslatedGC(gc, componentPositions.get(component));\r
-               component.render(tgc);\r
-               tgc.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLUE));\r
+               component.render(gc, visibleRegion);\r
+               gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_CYAN));\r
+               for (Pin p : component.getPins())\r
+               {\r
+                       Point pos = p.getPos();\r
+                       gc.fillOval(pos.x - 1, pos.y - 1, 2, 2);\r
+               }\r
        }\r
 \r
        private void mouseDown(Event e)\r
@@ -77,11 +92,10 @@ public class LogicUICanvas extends ZoomableCanvas
                if (e.button == 1)\r
                {\r
                        Point click = displayToWorldCoords(e.x, e.y);\r
-                       for (BasicGUIComponent component : components)\r
-                               if (component.getBounds().translate(componentPositions.get(component)).contains(click))\r
+                       for (GUIComponent component : model.getComponents())\r
+                               if (component.getBounds().contains(click) && component.clicked(click.x, click.y))\r
                                {\r
-                                       if (component.clicked(click.x, click.y))\r
-                                               redraw();\r
+                                       redraw();\r
                                        break;\r
                                }\r
                }\r
index db498ac..8bbfb1c 100644 (file)
@@ -7,7 +7,7 @@ import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.widgets.Display;\r
 import org.eclipse.swt.widgets.Shell;\r
 \r
-import era.mi.logic.timeline.Timeline;\r
+import era.mi.gui.model.ViewModel;\r
 import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasOverlay;\r
 import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;\r
 \r
@@ -18,18 +18,19 @@ import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInpu
  */\r
 public class LogicUIStandalone\r
 {\r
+       private ViewModel model;\r
+\r
        private final Display display;\r
        private final Shell shell;\r
        private final LogicUICanvas ui;\r
-       private Timeline timeline;\r
 \r
-       public LogicUIStandalone(Timeline timeline)\r
+       public LogicUIStandalone(ViewModel model)\r
        {\r
-               this.timeline = timeline;\r
+               this.model = model;\r
                display = new Display();\r
                shell = new Shell(display);\r
                shell.setLayout(new FillLayout());\r
-               ui = new LogicUICanvas(shell, SWT.NONE);\r
+               ui = new LogicUICanvas(shell, SWT.NONE, model);\r
 \r
                ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(ui);\r
                userInput.buttonDrag = 3;\r
@@ -49,39 +50,40 @@ public class LogicUIStandalone
        public void run()\r
        {\r
                AtomicBoolean running = new AtomicBoolean(true);\r
-               Thread simulationThread = new Thread(() ->\r
-               {\r
-                       while (running.get())\r
-                       {\r
-                               // always execute to keep timeline from "hanging behind" for too long\r
-                               timeline.executeUntil(timeline.laterThan(System.currentTimeMillis()), System.currentTimeMillis() + 10);\r
-                               long sleepTime;\r
-                               if (timeline.hasNext())\r
-                                       sleepTime = timeline.nextEventTime() - System.currentTimeMillis();\r
-                               else\r
-                                       sleepTime = 10;\r
-                               try\r
-                               {\r
-                                       if (sleepTime > 0)\r
-                                               Thread.sleep(sleepTime);\r
-                               }\r
-                               catch (InterruptedException e)\r
-                               {\r
-                               } // it is normal execution flow to be interrupted\r
-                       }\r
-               });\r
-               simulationThread.start();\r
-               timeline.addEventAddedListener(event ->\r
-               {\r
-                       if (event.getTiming() <= System.currentTimeMillis())\r
-                               simulationThread.interrupt();\r
-               });\r
+//             Thread simulationThread = new Thread(() ->\r
+//             {\r
+//                     while (running.get())\r
+//                     {\r
+//                             // always execute to keep timeline from "hanging behind" for too long\r
+//                             timeline.executeUntil(timeline.laterThan(System.currentTimeMillis()), System.currentTimeMillis() + 10);         \r
+//                             model.timeline.executeUpTo(System.currentTimeMillis(), System.currentTimeMillis() + 10);\r
+//                             long sleepTime;\r
+//                             if (model.timeline.hasNext())\r
+//                                     sleepTime = model.timeline.nextEventTime() - System.currentTimeMillis();\r
+//                             else\r
+//                                     sleepTime = 10;\r
+//                             try\r
+//                             {\r
+//                                     if (sleepTime > 0)\r
+//                                             Thread.sleep(sleepTime);\r
+//                             }\r
+//                             catch (InterruptedException e)\r
+//                             {\r
+//                             } // it is normal execution flow to be interrupted\r
+//                     }\r
+//             });\r
+//             simulationThread.start();\r
+//             model.timeline.addEventAddedListener(event ->\r
+//             {\r
+//                     if (event.getTiming() <= System.currentTimeMillis())\r
+//                             simulationThread.interrupt();\r
+//             });\r
 \r
                shell.open();\r
                while (!shell.isDisposed())\r
                        if (!display.readAndDispatch())\r
                                display.sleep();\r
                running.set(false);\r
-               simulationThread.interrupt();\r
+//             simulationThread.interrupt();\r
        }\r
 }
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/components/BasicGUIComponent.java b/LogicUI/src/era/mi/gui/components/BasicGUIComponent.java
deleted file mode 100644 (file)
index bf13d3e..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-package era.mi.gui.components;\r
-\r
-import era.mi.logic.wires.Wire.ReadEnd;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-\r
-public interface BasicGUIComponent\r
-{\r
-       /**\r
-        * Render this component to the given gc, at coordinates (0, 0).\r
-        */\r
-       public void render(GeneralGC gc);\r
-\r
-       /**\r
-        * Returns the bounds of this component. Used for calculating which component is clicked.\r
-        */\r
-       public Rectangle getBounds();\r
-\r
-       /**\r
-        * Called when this component is clicked. Relative coordinates of the click are given. Returns true if this component has to be redrawn.\r
-        */\r
-       public default boolean clicked(double x, double y)\r
-       {\r
-               return false;\r
-       }\r
-\r
-       // TODO this code will be replaced by code in BasicComponent.\r
-       /**\r
-        * Returns how many wire arrays are connected to this component. (Connections are static - they can't be removed and no new ones can be\r
-        * added)\r
-        */\r
-       public int getConnectedWireEndsCount();\r
-\r
-       /**\r
-        * Returns the n-th wire array connected to this component.\r
-        */\r
-       public ReadEnd getConnectedWireEnd(int connectionIndex);\r
-\r
-       /**\r
-        * Returns relative coordinates where the n-th wire array is connected to this component.\r
-        */\r
-       public Point getWireEndConnectionPoint(int connectionIndex);\r
-}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/components/GUIAndGate.java b/LogicUI/src/era/mi/gui/components/GUIAndGate.java
deleted file mode 100644 (file)
index 27cefc0..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-package era.mi.gui.components;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collections;\r
-import java.util.List;\r
-\r
-import era.mi.logic.components.gates.AndGate;\r
-import era.mi.logic.timeline.Timeline;\r
-import era.mi.logic.wires.Wire.ReadEnd;\r
-import era.mi.logic.wires.Wire.ReadWriteEnd;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Font;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-\r
-public class GUIAndGate extends AndGate implements BasicGUIComponent\r
-{\r
-       private static final String LABEL = "&";\r
-\r
-       private final int inputCount;\r
-       private final double height;\r
-       private final List<ReadEnd> connectedWireEnds;\r
-       private final List<Point> wireEndConnectionPoints;\r
-\r
-       public GUIAndGate(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd... in)\r
-       {\r
-               super(timeline, processTime, out, in);\r
-\r
-               List<ReadEnd> connectedWireEndsModifiable = new ArrayList<>();\r
-               List<Point> wireEndConnectionPointsModifiable = new ArrayList<>();\r
-\r
-               this.inputCount = in.length;\r
-               this.height = inputCount * 10;\r
-\r
-               {\r
-                       connectedWireEndsModifiable.addAll(Arrays.asList(in));\r
-                       double inputHeight = 5;\r
-                       for (int i = 0; i < inputCount; i++, inputHeight += 10)\r
-                               wireEndConnectionPointsModifiable.add(new Point(0, inputHeight));\r
-               }\r
-\r
-               connectedWireEndsModifiable.add(out);\r
-               wireEndConnectionPointsModifiable.add(new Point(20, height / 2));\r
-\r
-               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);\r
-               this.wireEndConnectionPoints = Collections.unmodifiableList(wireEndConnectionPointsModifiable);\r
-       }\r
-\r
-       @Override\r
-       public Rectangle getBounds()\r
-       {\r
-               return new Rectangle(0, 0, 20, height);\r
-       }\r
-\r
-       @Override\r
-       public void render(GeneralGC gc)\r
-       {\r
-               gc.drawRectangle(0, 0, 20, height);\r
-               Font oldFont = gc.getFont();\r
-               Font labelFont = new Font(oldFont.getName(), 5, oldFont.getStyle());\r
-               gc.setFont(labelFont);\r
-               Point textExtent = gc.textExtent(LABEL);\r
-               gc.drawText(LABEL, 10 - textExtent.x / 2, (height - textExtent.y) / 2, true);\r
-               gc.setFont(oldFont);\r
-       }\r
-\r
-       @Override\r
-       public int getConnectedWireEndsCount()\r
-       {\r
-               return connectedWireEnds.size();\r
-       }\r
-\r
-       @Override\r
-       public ReadEnd getConnectedWireEnd(int connectionIndex)\r
-       {\r
-               return connectedWireEnds.get(connectionIndex);\r
-       }\r
-\r
-       @Override\r
-       public Point getWireEndConnectionPoint(int connectionI)\r
-       {\r
-               return wireEndConnectionPoints.get(connectionI);\r
-       }\r
-}
\ No newline at end of file
index ff674c5..7bec909 100644 (file)
@@ -1,96 +1,6 @@
 package era.mi.gui.components;\r
 \r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import era.mi.logic.components.ManualSwitch;\r
-import era.mi.logic.timeline.Timeline;\r
-import era.mi.logic.types.Bit;\r
-import era.mi.logic.wires.Wire.ReadEnd;\r
-import era.mi.logic.wires.Wire.ReadWriteEnd;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Font;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-\r
-public class GUIManualSwitch extends ManualSwitch implements BasicGUIComponent\r
+public class GUIManualSwitch\r
 {\r
-       private static final Map<Bit, String> bitNames;\r
-       static\r
-       {\r
-               Map<Bit, String> bitNamesModifiable = new HashMap<>();\r
-               bitNamesModifiable.put(Bit.ONE, "1");\r
-               bitNamesModifiable.put(Bit.ZERO, "0");\r
-               bitNamesModifiable.put(Bit.Z, "Z");\r
-               bitNamesModifiable.put(Bit.U, "U");\r
-               bitNamesModifiable.put(Bit.X, "X");\r
-               bitNames = Collections.unmodifiableMap(bitNamesModifiable);\r
-       }\r
-\r
-       private final ReadEnd we;\r
-       private final List<ReadEnd> connectedWireEnds;\r
-       private final List<Point> wireEndConnectionPoints;\r
-\r
-       public GUIManualSwitch(Timeline timeline, ReadWriteEnd output)\r
-       {\r
-               super(timeline, output);\r
-\r
-               this.we = output;\r
-\r
-               List<ReadEnd> connectedWireEndsModifiable = new ArrayList<>();\r
-               List<Point> wireEndConnectionPointsModifiable = new ArrayList<>();\r
-\r
-               connectedWireEndsModifiable.add(output);\r
-               wireEndConnectionPointsModifiable.add(new Point(20, 7.5));\r
-\r
-               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);\r
-               this.wireEndConnectionPoints = Collections.unmodifiableList(wireEndConnectionPointsModifiable);\r
-       }\r
-\r
-       @Override\r
-       public Rectangle getBounds()\r
-       {\r
-               return new Rectangle(0, 0, 20, 15);\r
-       }\r
-\r
-       @Override\r
-       public void render(GeneralGC gc)\r
-       {\r
-               gc.drawRectangle(0, 0, 20, 15);\r
-               String label = bitNames.get(we.getValue());\r
-               Font oldFont = gc.getFont();\r
-               Font labelFont = new Font(oldFont.getName(), 6, oldFont.getStyle());\r
-               gc.setFont(labelFont);\r
-               Point textExtent = gc.textExtent(label);\r
-               gc.drawText(label, 10 - textExtent.x / 2, 7.5 - textExtent.y / 2, true);\r
-               gc.setFont(oldFont);\r
-       }\r
-\r
-       @Override\r
-       public boolean clicked(double x, double y)\r
-       {\r
-               timeline.addEvent((e) -> toggle(), (int) (System.currentTimeMillis() - timeline.getSimulationTime()));\r
-               return true;\r
-       }\r
-\r
-       @Override\r
-       public int getConnectedWireEndsCount()\r
-       {\r
-               return connectedWireEnds.size();\r
-       }\r
-\r
-       @Override\r
-       public ReadEnd getConnectedWireEnd(int connectionIndex)\r
-       {\r
-               return connectedWireEnds.get(connectionIndex);\r
-       }\r
 \r
-       @Override\r
-       public Point getWireEndConnectionPoint(int connectionI)\r
-       {\r
-               return wireEndConnectionPoints.get(connectionI);\r
-       }\r
-}
\ No newline at end of file
+}\r
diff --git a/LogicUI/src/era/mi/gui/components/GUIMerger.java b/LogicUI/src/era/mi/gui/components/GUIMerger.java
deleted file mode 100644 (file)
index 1525d0b..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-package era.mi.gui.components;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collections;\r
-import java.util.List;\r
-\r
-import era.mi.logic.components.Merger;\r
-import era.mi.logic.timeline.Timeline;\r
-import era.mi.logic.wires.Wire.ReadEnd;\r
-import era.mi.logic.wires.Wire.ReadWriteEnd;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-\r
-public class GUIMerger extends Merger implements BasicGUIComponent\r
-{\r
-       private final int inputCount;\r
-       private final double height;\r
-       private final List<ReadEnd> connectedWireEnds;\r
-       private final List<Point> WireEndConnectionPoints;\r
-\r
-       public GUIMerger(Timeline timeline, ReadWriteEnd union, ReadEnd... inputs)\r
-       {\r
-               super(timeline, union, inputs);\r
-\r
-               List<ReadEnd> connectedWireEndsModifiable = new ArrayList<>();\r
-               List<Point> WireEndConnectionPointsModifiable = new ArrayList<>();\r
-\r
-               this.inputCount = inputs.length;\r
-               this.height = (inputCount - 1) * 10;\r
-\r
-               {\r
-                       connectedWireEndsModifiable.addAll(Arrays.asList(inputs));\r
-                       double inputHeight = 0;\r
-                       for (int i = 0; i < inputCount; i++, inputHeight += 10)\r
-                               WireEndConnectionPointsModifiable.add(new Point(0, inputHeight));\r
-               }\r
-\r
-               connectedWireEndsModifiable.add(union);\r
-               WireEndConnectionPointsModifiable.add(new Point(20, height / 2));\r
-\r
-               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);\r
-               this.WireEndConnectionPoints = Collections.unmodifiableList(WireEndConnectionPointsModifiable);\r
-       }\r
-\r
-       @Override\r
-       public Rectangle getBounds()\r
-       {\r
-               return new Rectangle(0, 0, 20, height);\r
-       }\r
-\r
-       @Override\r
-       public void render(GeneralGC gc)\r
-       {\r
-               double inputHeight = 0;\r
-               for (int i = 0; i < inputCount; i++, inputHeight += 10)\r
-                       gc.drawLine(0, inputHeight, 10, inputHeight);\r
-               gc.drawLine(10, 0, 10, height);\r
-               gc.drawLine(10, height / 2, 20, height / 2);\r
-       }\r
-\r
-       @Override\r
-       public int getConnectedWireEndsCount()\r
-       {\r
-               return connectedWireEnds.size();\r
-       }\r
-\r
-       @Override\r
-       public ReadEnd getConnectedWireEnd(int connectionIndex)\r
-       {\r
-               return connectedWireEnds.get(connectionIndex);\r
-       }\r
-\r
-       @Override\r
-       public Point getWireEndConnectionPoint(int connectionI)\r
-       {\r
-               return WireEndConnectionPoints.get(connectionI);\r
-       }\r
-}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/components/GUIMux.java b/LogicUI/src/era/mi/gui/components/GUIMux.java
deleted file mode 100644 (file)
index 7fc1fa9..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-package era.mi.gui.components;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collections;\r
-import java.util.List;\r
-\r
-import era.mi.logic.components.Mux;\r
-import era.mi.logic.timeline.Timeline;\r
-import era.mi.logic.wires.Wire.ReadEnd;\r
-import era.mi.logic.wires.Wire.ReadWriteEnd;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-\r
-public class GUIMux extends Mux implements BasicGUIComponent\r
-{\r
-       private final double height;\r
-       private final List<ReadEnd> connectedWireEnds;\r
-       private final List<Point> WireEndConnectionPoints;\r
-\r
-       public GUIMux(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd select, ReadEnd... inputs)\r
-       {\r
-               super(timeline, processTime, out, select, inputs);\r
-\r
-               double height = inputs.length * 5;\r
-               if (height < 10)\r
-                       height = 10;\r
-               this.height = height;\r
-\r
-               List<ReadEnd> connectedWireEndsModifiable = new ArrayList<>();\r
-               List<Point> WireEndConnectionPointsModifiable = new ArrayList<>();\r
-\r
-               connectedWireEndsModifiable.add(out);\r
-               WireEndConnectionPointsModifiable.add(new Point(20, 10 + height / 2));\r
-\r
-               connectedWireEndsModifiable.add(select);\r
-               WireEndConnectionPointsModifiable.add(new Point(10, 5));\r
-\r
-               {\r
-                       connectedWireEndsModifiable.addAll(Arrays.asList(inputs));\r
-                       double inputHeightIncrement = (height + 20) / inputs.length;\r
-                       double inputHeight = inputHeightIncrement / 2;\r
-                       for (int i = 0; i < inputs.length; i++, inputHeight += inputHeightIncrement)\r
-                               WireEndConnectionPointsModifiable.add(new Point(0, inputHeight));\r
-               }\r
-\r
-               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);\r
-               this.WireEndConnectionPoints = Collections.unmodifiableList(WireEndConnectionPointsModifiable);\r
-       }\r
-\r
-       @Override\r
-       public Rectangle getBounds()\r
-       {\r
-               return new Rectangle(0, 0, 20, height + 20);\r
-       }\r
-\r
-       @Override\r
-       public void render(GeneralGC gc)\r
-       {\r
-               gc.drawPolygon(new double[] { 0, 0, 20, 10, 20, height + 10, 0, height + 20 });\r
-       }\r
-\r
-       @Override\r
-       public int getConnectedWireEndsCount()\r
-       {\r
-               return connectedWireEnds.size();\r
-       }\r
-\r
-       @Override\r
-       public ReadEnd getConnectedWireEnd(int connectionIndex)\r
-       {\r
-               return connectedWireEnds.get(connectionIndex);\r
-       }\r
-\r
-       @Override\r
-       public Point getWireEndConnectionPoint(int connectionI)\r
-       {\r
-               return WireEndConnectionPoints.get(connectionI);\r
-       }\r
-}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/components/GUINotGate.java b/LogicUI/src/era/mi/gui/components/GUINotGate.java
deleted file mode 100644 (file)
index e860d9b..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-package era.mi.gui.components;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.List;\r
-\r
-import era.mi.logic.components.gates.NotGate;\r
-import era.mi.logic.timeline.Timeline;\r
-import era.mi.logic.wires.Wire.ReadEnd;\r
-import era.mi.logic.wires.Wire.ReadWriteEnd;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Font;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-\r
-public class GUINotGate extends NotGate implements BasicGUIComponent\r
-{\r
-       private static final String LABEL = "\u22651";// >=1\r
-\r
-       private final List<ReadEnd> connectedWireEnds;\r
-       private final List<Point> WireEndConnectionPoints;\r
-\r
-       public GUINotGate(Timeline timeline, int processTime, ReadEnd in, ReadWriteEnd out)\r
-       {\r
-               super(timeline, processTime, in, out);\r
-\r
-               List<ReadEnd> connectedWireEndsModifiable = new ArrayList<>();\r
-               List<Point> WireEndConnectionPointsModifiable = new ArrayList<>();\r
-\r
-               connectedWireEndsModifiable.add(in);\r
-               WireEndConnectionPointsModifiable.add(new Point(0, 5));\r
-\r
-               connectedWireEndsModifiable.add(out);\r
-               WireEndConnectionPointsModifiable.add(new Point(20, 5));\r
-\r
-               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);\r
-               this.WireEndConnectionPoints = Collections.unmodifiableList(WireEndConnectionPointsModifiable);\r
-       }\r
-\r
-       @Override\r
-       public Rectangle getBounds()\r
-       {\r
-               return new Rectangle(0, 0, 20, 10);\r
-       }\r
-\r
-       @Override\r
-       public void render(GeneralGC gc)\r
-       {\r
-               gc.drawRectangle(0, 0, 17, 10);\r
-               Font oldFont = gc.getFont();\r
-               Font labelFont = new Font(oldFont.getName(), 5, oldFont.getStyle());\r
-               gc.setFont(labelFont);\r
-               Point textExtent = gc.textExtent(LABEL);\r
-               gc.drawText(LABEL, 8.5 - textExtent.x / 2, 5 - textExtent.y / 2, true);\r
-               gc.setFont(oldFont);\r
-               gc.drawOval(17, 3.5, 3, 3);\r
-       }\r
-\r
-       @Override\r
-       public int getConnectedWireEndsCount()\r
-       {\r
-               return connectedWireEnds.size();\r
-       }\r
-\r
-       @Override\r
-       public ReadEnd getConnectedWireEnd(int connectionIndex)\r
-       {\r
-               return connectedWireEnds.get(connectionIndex);\r
-       }\r
-\r
-       @Override\r
-       public Point getWireEndConnectionPoint(int connectionI)\r
-       {\r
-               return WireEndConnectionPoints.get(connectionI);\r
-       }\r
-}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/components/GUIOrGate.java b/LogicUI/src/era/mi/gui/components/GUIOrGate.java
deleted file mode 100644 (file)
index 573c640..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-package era.mi.gui.components;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collections;\r
-import java.util.List;\r
-\r
-import era.mi.logic.components.gates.OrGate;\r
-import era.mi.logic.timeline.Timeline;\r
-import era.mi.logic.wires.Wire.ReadEnd;\r
-import era.mi.logic.wires.Wire.ReadWriteEnd;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Font;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-\r
-public class GUIOrGate extends OrGate implements BasicGUIComponent\r
-{\r
-       private static final String LABEL = "\u22651";// >=1\r
-\r
-       private final int inputCount;\r
-       private final double height;\r
-       private final List<ReadEnd> connectedWireEnds;\r
-       private final List<Point> WireEndConnectionPoints;\r
-\r
-       public GUIOrGate(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd... in)\r
-       {\r
-               super(timeline, processTime, out, in);\r
-\r
-               List<ReadEnd> connectedWireEndsModifiable = new ArrayList<>();\r
-               List<Point> WireEndConnectionPointsModifiable = new ArrayList<>();\r
-\r
-               this.inputCount = in.length;\r
-               this.height = inputCount * 10;\r
-\r
-               {\r
-                       connectedWireEndsModifiable.addAll(Arrays.asList(in));\r
-                       double inputHeight = 5;\r
-                       for (int i = 0; i < inputCount; i++, inputHeight += 10)\r
-                               WireEndConnectionPointsModifiable.add(new Point(0, inputHeight));\r
-               }\r
-\r
-               connectedWireEndsModifiable.add(out);\r
-               WireEndConnectionPointsModifiable.add(new Point(20, height / 2));\r
-\r
-               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);\r
-               this.WireEndConnectionPoints = Collections.unmodifiableList(WireEndConnectionPointsModifiable);\r
-       }\r
-\r
-       @Override\r
-       public Rectangle getBounds()\r
-       {\r
-               return new Rectangle(0, 0, 20, height);\r
-       }\r
-\r
-       @Override\r
-       public void render(GeneralGC gc)\r
-       {\r
-               gc.drawRectangle(0, 0, 20, height);\r
-               Font oldFont = gc.getFont();\r
-               Font labelFont = new Font(oldFont.getName(), 5, oldFont.getStyle());\r
-               gc.setFont(labelFont);\r
-               Point textExtent = gc.textExtent(LABEL);\r
-               gc.drawText(LABEL, 10 - textExtent.x / 2, (height - textExtent.y) / 2, true);\r
-               gc.setFont(oldFont);\r
-       }\r
-\r
-       @Override\r
-       public int getConnectedWireEndsCount()\r
-       {\r
-               return connectedWireEnds.size();\r
-       }\r
-\r
-       @Override\r
-       public ReadEnd getConnectedWireEnd(int connectionIndex)\r
-       {\r
-               return connectedWireEnds.get(connectionIndex);\r
-       }\r
-\r
-       @Override\r
-       public Point getWireEndConnectionPoint(int connectionI)\r
-       {\r
-               return WireEndConnectionPoints.get(connectionI);\r
-       }\r
-}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/components/GUISplitter.java b/LogicUI/src/era/mi/gui/components/GUISplitter.java
deleted file mode 100644 (file)
index d229ac1..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-package era.mi.gui.components;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collections;\r
-import java.util.List;\r
-\r
-import era.mi.logic.components.Splitter;\r
-import era.mi.logic.timeline.Timeline;\r
-import era.mi.logic.wires.Wire.ReadEnd;\r
-import era.mi.logic.wires.Wire.ReadWriteEnd;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-\r
-public class GUISplitter extends Splitter implements BasicGUIComponent\r
-{\r
-       private final int outputCount;\r
-       private final double height;\r
-       private final List<ReadEnd> connectedWireEnds;\r
-       private final List<Point> WireEndConnectionPoints;\r
-\r
-       public GUISplitter(Timeline timeline, ReadEnd input, ReadWriteEnd... outputs)\r
-       {\r
-               super(timeline, input, outputs);\r
-\r
-               List<ReadEnd> connectedWireEndsModifiable = new ArrayList<>();\r
-               List<Point> WireEndConnectionPointsModifiable = new ArrayList<>();\r
-\r
-               this.outputCount = outputs.length;\r
-               this.height = (outputCount - 1) * 10;\r
-\r
-               connectedWireEndsModifiable.add(input);\r
-               WireEndConnectionPointsModifiable.add(new Point(0, height / 2));\r
-\r
-               {\r
-                       connectedWireEndsModifiable.addAll(Arrays.asList(outputs));\r
-                       double outputHeight = 0;\r
-                       for (int i = 0; i < outputCount; i++, outputHeight += 10)\r
-                               WireEndConnectionPointsModifiable.add(new Point(20, outputHeight));\r
-               }\r
-\r
-               this.connectedWireEnds = Collections.unmodifiableList(connectedWireEndsModifiable);\r
-               this.WireEndConnectionPoints = Collections.unmodifiableList(WireEndConnectionPointsModifiable);\r
-       }\r
-\r
-       @Override\r
-       public Rectangle getBounds()\r
-       {\r
-               return new Rectangle(0, 0, 20, height);\r
-       }\r
-\r
-       @Override\r
-       public void render(GeneralGC gc)\r
-       {\r
-               gc.drawLine(0, height / 2, 10, height / 2);\r
-               gc.drawLine(10, 0, 10, height);\r
-               double outputHeight = 0;\r
-               for (int i = 0; i < outputCount; i++, outputHeight += 10)\r
-                       gc.drawLine(10, outputHeight, 20, outputHeight);\r
-       }\r
-\r
-       @Override\r
-       public int getConnectedWireEndsCount()\r
-       {\r
-               return connectedWireEnds.size();\r
-       }\r
-\r
-       @Override\r
-       public ReadEnd getConnectedWireEnd(int connectionIndex)\r
-       {\r
-               return connectedWireEnds.get(connectionIndex);\r
-       }\r
-\r
-       @Override\r
-       public Point getWireEndConnectionPoint(int connectionI)\r
-       {\r
-               return WireEndConnectionPoints.get(connectionI);\r
-       }\r
-}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/examples/Playground.java b/LogicUI/src/era/mi/gui/examples/Playground.java
new file mode 100644 (file)
index 0000000..78524b7
--- /dev/null
@@ -0,0 +1,37 @@
+package era.mi.gui.examples;
+
+import org.eclipse.swt.SWT;
+
+import era.mi.gui.LogicUIStandalone;
+import era.mi.gui.model.ViewModel;
+import era.mi.gui.model.components.GUIAndGate;
+import era.mi.gui.model.components.GUINotGate;
+import era.mi.gui.model.wires.GUIWire;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+
+public class Playground
+{
+       private static final int WIRE_DELAY = 10;
+       private static final int OR_DELAY = 50;
+       private static final int NOT_DELAY = 50;
+
+       public static void main(String[] args)
+       {
+               ViewModel model = new ViewModel();
+               LogicUIStandalone ui = new LogicUIStandalone(model);
+               addComponentsAndWires(ui, model);
+               ui.run();
+       }
+
+       public static void addComponentsAndWires(LogicUIStandalone ui, ViewModel model)
+       {
+               GUIAndGate andGate = new GUIAndGate(model);
+               andGate.moveTo(10, 10);
+               GUINotGate notGate = new GUINotGate(model);
+               notGate.moveTo(10, 40);
+
+               new GUIWire(model, andGate.getPins().get(0), notGate.getPins().get(1), new Point(20, 50));
+
+               ui.getLogicUICanvas().addListener(SWT.KeyDown, e -> notGate.moveTo(150, 10));
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/examples/RSLatchGUIExample.java b/LogicUI/src/era/mi/gui/examples/RSLatchGUIExample.java
deleted file mode 100644 (file)
index 405088d..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-package era.mi.gui.examples;\r
-\r
-import era.mi.gui.LogicUICanvas;\r
-import era.mi.gui.LogicUIStandalone;\r
-import era.mi.gui.components.GUIManualSwitch;\r
-import era.mi.gui.components.GUINotGate;\r
-import era.mi.gui.components.GUIOrGate;\r
-import era.mi.gui.wires.WireConnectionPoint;\r
-import era.mi.logic.timeline.Timeline;\r
-import era.mi.logic.wires.Wire;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-\r
-public class RSLatchGUIExample\r
-{\r
-       private static final int WIRE_DELAY = 10;\r
-       private static final int OR_DELAY = 50;\r
-       private static final int NOT_DELAY = 50;\r
-\r
-       public static void main(String[] args)\r
-       {\r
-               Timeline t = new Timeline(11);\r
-               t.setTimeFunction(() -> System.currentTimeMillis()); // real time simulation\r
-               LogicUIStandalone ui = new LogicUIStandalone(t);\r
-               addComponentsAndWires(ui.getLogicUICanvas(), t);\r
-               ui.run();\r
-       }\r
-\r
-       public static void addComponentsAndWires(LogicUICanvas ui, Timeline t)\r
-       {\r
-               Wire r = new Wire(t, 1, WIRE_DELAY);\r
-               Wire s = new Wire(t, 1, WIRE_DELAY);\r
-               Wire t2 = new Wire(t, 1, WIRE_DELAY);\r
-               Wire t1 = new Wire(t, 1, WIRE_DELAY);\r
-               Wire q = new Wire(t, 1, WIRE_DELAY);\r
-               Wire nq = new Wire(t, 1, WIRE_DELAY);\r
-\r
-               GUIManualSwitch rIn = ui.addComponent(new GUIManualSwitch(t, r.createReadWriteEnd()), 100, 100);\r
-               GUIManualSwitch sIn = ui.addComponent(new GUIManualSwitch(t, s.createReadWriteEnd()), 100, 200);\r
-               GUIOrGate or1 = ui.addComponent(new GUIOrGate(t, OR_DELAY, t1.createReadWriteEnd(), r.createReadOnlyEnd(), nq.createReadOnlyEnd()),\r
-                               160, 102.5);\r
-               GUIOrGate or2 = ui.addComponent(new GUIOrGate(t, OR_DELAY, t2.createReadWriteEnd(), q.createReadOnlyEnd(), s.createReadOnlyEnd()),\r
-                               160, 192.5);\r
-               GUINotGate not1 = ui.addComponent(new GUINotGate(t, NOT_DELAY, t1.createReadOnlyEnd(), q.createReadWriteEnd()), 200, 107.5);\r
-               GUINotGate not2 = ui.addComponent(new GUINotGate(t, NOT_DELAY, t2.createReadOnlyEnd(), nq.createReadWriteEnd()), 200, 197.5);\r
-\r
-               WireConnectionPoint p1 = ui.addComponent(new WireConnectionPoint(q, 3), 250, 112.5);\r
-               WireConnectionPoint p2 = ui.addComponent(new WireConnectionPoint(nq, 3), 250, 202.5);\r
-               WireConnectionPoint o1 = ui.addComponent(new WireConnectionPoint(q, 1), 270, 112.5);\r
-               WireConnectionPoint o2 = ui.addComponent(new WireConnectionPoint(nq, 1), 270, 202.5);\r
-\r
-               ui.addWire(rIn, 0, or1, 0);\r
-               ui.addWire(sIn, 0, or2, 1);\r
-               ui.addWire(or1, 2, not1, 0);\r
-               ui.addWire(or2, 2, not2, 0);\r
-               ui.addWire(not1, 1, p1, 0);\r
-               ui.addWire(not2, 1, p2, 0);\r
-               ui.addWire(p1, 1, or2, 0, new Point(250, 130), new Point(140, 185), new Point(140, 197.5));\r
-               ui.addWire(p2, 1, or1, 1, new Point(250, 185), new Point(140, 130), new Point(140, 117.5));\r
-               ui.addWire(p1, 2, o1, 0);\r
-               ui.addWire(p2, 2, o2, 0);\r
-       }\r
-}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/ViewModel.java b/LogicUI/src/era/mi/gui/model/ViewModel.java
new file mode 100644 (file)
index 0000000..7eb55c1
--- /dev/null
@@ -0,0 +1,110 @@
+package era.mi.gui.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+import era.mi.gui.model.components.GUIComponent;
+import era.mi.gui.model.wires.GUIWire;
+
+public class ViewModel
+{
+       private final List<GUIComponent> components;
+       private final List<GUIComponent> componentsUnmodifiable;
+       private final List<GUIWire> wires;
+       private final List<GUIWire> wiresUnmodifiable;
+
+       private final List<Consumer<? super GUIComponent>> componentAddedListeners;
+       private final List<Consumer<? super GUIComponent>> componentRemovedListeners;
+       private final List<Consumer<? super GUIWire>> wireAddedListeners;
+       private final List<Consumer<? super GUIWire>> wireRemovedListeners;
+
+       public ViewModel()
+       {
+               components = new ArrayList<>();
+               componentsUnmodifiable = Collections.unmodifiableList(components);
+               wires = new ArrayList<>();
+               wiresUnmodifiable = Collections.unmodifiableList(wires);
+
+               componentAddedListeners = new ArrayList<>();
+               componentRemovedListeners = new ArrayList<>();
+               wireAddedListeners = new ArrayList<>();
+               wireRemovedListeners = new ArrayList<>();
+       }
+
+       /**
+        * Adds the given component to the list of components and calls all componentAddedListeners. Don't call this method from application
+        * code as it is automatically called in GUIComponent::new.
+        */
+       public void componentCreated(GUIComponent component)
+       {
+               if (components.contains(component))
+                       throw new IllegalStateException("Don't add the same component twice!");
+               components.add(component);
+               callComponentAddedListeners(component);
+       }
+
+       /**
+        * Removes the given component from the list of components and calls all componentRemovedListeners. Don't call this method from
+        * application code as it is automatically called in GUIComponent::destroy.
+        */
+       public void componentDestroyed(GUIComponent component)
+       {
+               if (!components.contains(component))
+                       throw new IllegalStateException("Don't remove the same component twice!");
+               components.remove(component);
+               callComponentRemovedListeners(component);
+       }
+
+       /**
+        * Adds the given component to the list of components and calls all componentAddedListeners. Don't call this method from application
+        * code as it is automatically called in GUIComponent::new.
+        */
+       public void wireCreated(GUIWire wire)
+       {
+               if (wires.contains(wire))
+                       throw new IllegalStateException("Don't add the same wire twice!");
+               wires.add(wire);
+               callWireAddedListeners(wire);
+       }
+
+       /**
+        * Removes the given component from the list of components and calls all componentRemovedListeners. Don't call this method from
+        * application code as it is automatically called in GUIComponent::destroy.
+        */
+       public void wireDestroyed(GUIWire wire)
+       {
+               if (!wires.contains(wire))
+                       throw new IllegalStateException("Don't remove the same wire twice!");
+               wires.remove(wire);
+               callWireRemovedListeners(wire);
+       }
+
+       public List<GUIComponent> getComponents()
+       {
+               return componentsUnmodifiable;
+       }
+
+       public List<GUIWire> getWires()
+       {
+               return wiresUnmodifiable;
+       }
+
+       // @formatter:off
+       public void addComponentAddedListener     (Consumer<? super GUIComponent> listener){componentAddedListeners  .add   (listener);}
+       public void addComponentRemovedListener   (Consumer<? super GUIComponent> listener){componentRemovedListeners.add   (listener);}
+       public void addWireAddedListener          (Consumer<? super GUIWire     > listener){wireAddedListeners       .add   (listener);}
+       public void addWireRemovedListener        (Consumer<? super GUIWire     > listener){wireRemovedListeners     .add   (listener);}
+
+       public void removeComponentAddedListener  (Consumer<? super GUIComponent> listener){componentAddedListeners  .remove(listener);}
+       public void removeComponentRemovedListener(Consumer<? super GUIComponent> listener){componentRemovedListeners.remove(listener);}
+       public void removeWireAddedListener       (Consumer<? super GUIWire     > listener){wireAddedListeners       .remove(listener);}
+       public void removeWireRemovedListener     (Consumer<? super GUIWire     > listener){wireRemovedListeners     .remove(listener);}
+
+       private void callComponentAddedListeners  (GUIComponent c) {componentAddedListeners  .forEach(l -> l.accept(c));}
+       private void callComponentRemovedListeners(GUIComponent c) {componentRemovedListeners.forEach(l -> l.accept(c));}
+       private void callWireAddedListeners       (GUIWire w     ) {wireAddedListeners       .forEach(l -> l.accept(w));}
+       private void callWireRemovedListeners     (GUIWire w     ) {wireRemovedListeners     .forEach(l -> l.accept(w));}
+       // @formatter:on
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/components/GUIAndGate.java b/LogicUI/src/era/mi/gui/model/components/GUIAndGate.java
new file mode 100644 (file)
index 0000000..4f69d36
--- /dev/null
@@ -0,0 +1,12 @@
+package era.mi.gui.model.components;
+
+import era.mi.gui.model.ViewModel;
+
+public class GUIAndGate extends RectangularShapedGUIGate
+{
+       public GUIAndGate(ViewModel model)
+       {
+               super(model, "&", false);
+               setInputCount(2);
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/components/GUIComponent.java b/LogicUI/src/era/mi/gui/model/components/GUIComponent.java
new file mode 100644 (file)
index 0000000..dc06461
--- /dev/null
@@ -0,0 +1,118 @@
+package era.mi.gui.model.components;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+import era.mi.gui.model.ViewModel;
+import era.mi.gui.model.wires.Pin;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public abstract class GUIComponent
+{
+       protected final ViewModel model;
+       private final Rectangle bounds;
+       private final List<Pin> pins;
+       protected final List<Pin> pinsUnmodifiable;
+
+       private final List<Consumer<? super GUIComponent>> componentChangedListeners;
+       private final List<Consumer<? super GUIComponent>> componentMovedListeners;
+       private final List<Consumer<? super Pin>> pinAddedListeners;
+       private final List<Consumer<? super Pin>> pinRemovedListeners;
+
+       public GUIComponent(ViewModel model)
+       {
+               this.model = model;
+               this.bounds = new Rectangle(0, 0, 0, 0);
+               this.pins = new ArrayList<>();
+               this.pinsUnmodifiable = Collections.unmodifiableList(pins);
+
+               this.componentChangedListeners = new ArrayList<>();
+               this.componentMovedListeners = new ArrayList<>();
+               this.pinAddedListeners = new ArrayList<>();
+               this.pinRemovedListeners = new ArrayList<>();
+
+               model.componentCreated(this);
+       }
+
+       public void destroy()
+       {
+               pins.forEach(p -> pinRemovedListeners.forEach(l -> l.accept(p)));
+               model.componentDestroyed(this);
+       }
+
+       public void moveTo(double x, double y)
+       {
+               bounds.x = x;
+               bounds.y = y;
+               callComponentMovedListeners();
+       }
+
+       /**
+        * Returns the bounds of this component. Used for calculating which component is clicked.
+        */
+       public Rectangle getBounds()
+       {
+               return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
+       }
+
+       /**
+        * Called when this component is clicked. Absolute coordinates of the click are given. Returns true if this component consumed this
+        * click.
+        */
+       public boolean clicked(double x, double y)
+       {
+               return false;
+       }
+
+       /**
+        * Returns a list of pins of this component.
+        */
+       public List<Pin> getPins()
+       {
+               return pinsUnmodifiable;
+       }
+
+       // @formatter:off
+       public void addComponentChangedListener   (Consumer<? super GUIComponent> listener) {componentChangedListeners.add   (listener);}
+       public void addComponentMovedListener     (Consumer<? super GUIComponent> listener) {componentMovedListeners  .add   (listener);}
+       public void addPinAddedListener           (Consumer<? super Pin         > listener) {pinAddedListeners        .add   (listener);}
+       public void addPinRemovedListener         (Consumer<? super Pin         > listener) {pinRemovedListeners      .add   (listener);}
+
+       public void removeComponentChangedListener(Consumer<? super GUIComponent> listener) {componentChangedListeners.remove(listener);}
+       public void removeComponentMovedListener  (Consumer<? super GUIComponent> listener) {componentMovedListeners  .remove(listener);}
+       public void removePinAddedListener        (Consumer<? super Pin         > listener) {pinAddedListeners        .remove(listener);}
+       public void removePinRemovedListener      (Consumer<? super Pin         > listener) {pinRemovedListeners      .remove(listener);}
+
+       protected void callComponentChangedListeners(     ) {componentChangedListeners.forEach(l -> l.accept(this));}
+       private   void callComponentMovedListeners  (     ) {componentMovedListeners  .forEach(l -> l.accept(this));}
+       private   void callPinAddedListeners        (Pin p) {pinAddedListeners        .forEach(l -> l.accept(p   ));}
+       private   void callPinRemovedListeners      (Pin p) {pinRemovedListeners      .forEach(l -> l.accept(p   ));}
+       // @form  atter:on
+
+       /**
+        * Render this component to the given gc.
+        */
+       public abstract void render(GeneralGC gc, Rectangle visibleRegion);
+
+       protected void setSize(double width, double height)
+       {
+               bounds.width = width;
+               bounds.height = height;
+               callComponentChangedListeners();
+       }
+
+       protected void addPin(Pin pin)
+       {
+               pins.add(pin);
+               callPinAddedListeners(pin);
+       }
+
+       protected void removePin(Pin pin)
+       {
+               pins.remove(pin);
+               callPinRemovedListeners(pin);
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java b/LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java
new file mode 100644 (file)
index 0000000..870a55a
--- /dev/null
@@ -0,0 +1,56 @@
+package era.mi.gui.model.components;
+
+import era.mi.gui.model.ViewModel;
+import era.mi.gui.model.wires.Pin;
+import era.mi.logic.components.ManualSwitch;
+import era.mi.logic.types.BitVectorFormatter;
+import era.mi.logic.wires.Wire.ReadEnd;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Font;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class GUIManualSwitch extends GUIComponent
+{
+       private static final double width = 20;
+       private static final double height = 15;
+       private static final double fontHeight = 5;
+
+       private ManualSwitch logicSwitch;
+       private ReadEnd end;
+
+       public GUIManualSwitch(ViewModel model)
+       {
+               super(model);
+               setSize(width, height);
+               addPin(new Pin(this, width, height / 2));
+       }
+
+       @Override
+       public void render(GeneralGC gc, Rectangle visibleRegion)
+       {
+               gc.drawRectangle(0, 0, width, height);
+               String label = BitVectorFormatter.formatValueAsString(end);
+               Font oldFont = gc.getFont();
+               Font labelFont = new Font(oldFont.getName(), fontHeight, oldFont.getStyle());
+               gc.setFont(labelFont);
+               Point textExtent = gc.textExtent(label);
+               gc.drawText(label, (width - textExtent.x) / 2, (height - textExtent.y) / 2, true);
+               gc.setFont(oldFont);
+       }
+
+       public void setLogicModelBinding(ManualSwitch logicSwitch, ReadEnd end)
+       {
+               this.logicSwitch = logicSwitch;
+               this.end = end;
+               // TODO when ManualSwitch supports it, add listeners
+               end.addObserver((i, o) -> callComponentChangedListeners());
+       }
+
+       @Override
+       public boolean clicked(double x, double y)
+       {
+               logicSwitch.toggle();
+               return true;
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/components/GUINotGate.java b/LogicUI/src/era/mi/gui/model/components/GUINotGate.java
new file mode 100644 (file)
index 0000000..5f41592
--- /dev/null
@@ -0,0 +1,12 @@
+package era.mi.gui.model.components;
+
+import era.mi.gui.model.ViewModel;
+
+public class GUINotGate extends RectangularShapedGUIGate
+{
+       public GUINotGate(ViewModel model)
+       {
+               super(model, "1", true);
+               setInputCount(1);
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/components/GUIOrGate.java b/LogicUI/src/era/mi/gui/model/components/GUIOrGate.java
new file mode 100644 (file)
index 0000000..df64bba
--- /dev/null
@@ -0,0 +1,12 @@
+package era.mi.gui.model.components;
+
+import era.mi.gui.model.ViewModel;
+
+public class GUIOrGate extends RectangularShapedGUIGate
+{
+       public GUIOrGate(ViewModel model)
+       {
+               super(model, "\u22651", false);// ">=1"
+               setInputCount(2);
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/components/RectangularShapedGUIGate.java b/LogicUI/src/era/mi/gui/model/components/RectangularShapedGUIGate.java
new file mode 100644 (file)
index 0000000..c7d0b34
--- /dev/null
@@ -0,0 +1,74 @@
+package era.mi.gui.model.components;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import era.mi.gui.model.ViewModel;
+import era.mi.gui.model.wires.MovablePin;
+import era.mi.gui.model.wires.Pin;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Font;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class RectangularShapedGUIGate extends GUIComponent
+{
+       private static final double width = 20;
+       private static final double pinDistance = 10;
+       private static final double fontHeight = 5;
+       private static final double invertedCircleDiam = 3.5;
+
+       private final String label;
+       private final boolean isInverted;
+       private final double rectWidth;
+
+       private MovablePin outputPin;
+       private final List<Pin> inputPins;
+
+       protected RectangularShapedGUIGate(ViewModel model, String label, boolean isInverted)
+       {
+               super(model);
+               this.label = label;
+               this.isInverted = isInverted;
+               this.rectWidth = width - (isInverted ? invertedCircleDiam : 0);
+               this.outputPin = new MovablePin(this, width, 0);
+               addPin(outputPin);
+               this.inputPins = new ArrayList<>();
+               setInputCount(1);
+       }
+
+       protected void setInputCount(int inputCount)
+       {
+               int oldInputCount = inputPins.size();
+               setSize(width, inputCount * pinDistance);
+               if (oldInputCount > inputCount)
+                       while (inputPins.size() > inputCount)
+                               removePin(inputPins.get(inputCount));
+               else if (oldInputCount < inputCount)
+                       for (int i = oldInputCount; i < inputCount; i++)
+                       {
+                               Pin pin = new Pin(this, 0, pinDistance / 2 + i * pinDistance);
+                               inputPins.add(pin);
+                               addPin(pin);
+                       }
+               outputPin.setRelPos(width, inputCount * pinDistance / 2);
+       }
+
+       @Override
+       public void render(GeneralGC gc, Rectangle visibleRegion)
+       {
+               double posX = getBounds().x;
+               double posY = getBounds().y;
+
+               double height = inputPins.size() * pinDistance;
+               gc.drawRectangle(posX, posY, rectWidth, height);
+               Font oldFont = gc.getFont();
+               Font labelFont = new Font(oldFont.getName(), fontHeight, oldFont.getStyle());
+               gc.setFont(labelFont);
+               Point textExtent = gc.textExtent(label);
+               gc.drawText(label, posX + (rectWidth - textExtent.x) / 2, posY + (height - textExtent.y) / 2, true);
+               gc.setFont(oldFont);
+               if (isInverted)
+                       gc.drawOval(posX + rectWidth, posY + (height - invertedCircleDiam) / 2, invertedCircleDiam, invertedCircleDiam);
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/wires/GUIWire.java b/LogicUI/src/era/mi/gui/model/wires/GUIWire.java
new file mode 100644 (file)
index 0000000..c2f03ee
--- /dev/null
@@ -0,0 +1,86 @@
+package era.mi.gui.model.wires;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import era.mi.gui.ColorHelper;
+import era.mi.gui.model.ViewModel;
+import era.mi.logic.types.BitVectorFormatter;
+import era.mi.logic.wires.Wire.ReadEnd;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+
+public class GUIWire
+{
+       private final ViewModel model;
+       private Pin pin1;
+       private Pin pin2;
+       private double[] path;
+
+       private final List<Consumer<? super GUIWire>> wireChangedListeners;
+
+       private ReadEnd end;
+
+       public GUIWire(ViewModel model, Pin pin1, Pin pin2, Point... path)
+       {
+               this.model = model;
+               this.path = new double[path.length * 2 + 4];
+               for (int srcI = 0, dstI = 2; srcI < path.length; srcI++, dstI += 2)
+               {
+                       this.path[dstI + 0] = path[srcI].x;
+                       this.path[dstI + 1] = path[srcI].y;
+               }
+
+               this.pin1 = pin1;
+               this.pin2 = pin2;
+
+               wireChangedListeners = new ArrayList<>();
+
+               pin1.addPinMovedListener(p -> pin1Moved());
+               pin2.addPinMovedListener(p -> pin2Moved());
+               pin1Moved();
+               pin2Moved();
+
+               model.wireCreated(this);
+       }
+
+       private void pin1Moved()
+       {
+               Point pos = pin1.getPos();
+               this.path[0] = pos.x;
+               this.path[1] = pos.y;
+       }
+
+       private void pin2Moved()
+       {
+               Point pos = pin2.getPos();
+               this.path[this.path.length - 2] = pos.x;
+               this.path[this.path.length - 1] = pos.y;
+       }
+
+       public void destroy()
+       {
+               model.wireDestroyed(this);
+       }
+
+       public void render(GeneralGC gc)
+       {
+               ColorHelper.executeWithDifferentForeground(gc, BitVectorFormatter.formatAsColor(end), () -> gc.drawPolyline(path));
+       }
+
+       public void setLogicModelBinding(ReadEnd end)
+       {
+               this.end = end;
+               end.addObserver((i, o) -> callWireChangedListeners());
+       }
+
+       // @formatter:off
+       public void addWireChangedListener   (Consumer<? super GUIWire> listener) {wireChangedListeners.add   (listener);}
+
+       public void removeWireChangedListener(Consumer<? super GUIWire> listener) {wireChangedListeners.remove(listener);}
+
+       private void callWireChangedListeners() {wireChangedListeners.forEach(l -> l.accept(this));}
+       // @formatter:on
+
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/wires/MovablePin.java b/LogicUI/src/era/mi/gui/model/wires/MovablePin.java
new file mode 100644 (file)
index 0000000..673b257
--- /dev/null
@@ -0,0 +1,17 @@
+package era.mi.gui.model.wires;
+
+import era.mi.gui.model.components.GUIComponent;
+
+public class MovablePin extends Pin
+{
+       public MovablePin(GUIComponent component, double relX, double relY)
+       {
+               super(component, relX, relY);
+       }
+
+       @Override
+       public void setRelPos(double relX, double relY)
+       {
+               super.setRelPos(relX, relY);
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/wires/Pin.java b/LogicUI/src/era/mi/gui/model/wires/Pin.java
new file mode 100644 (file)
index 0000000..44ea374
--- /dev/null
@@ -0,0 +1,66 @@
+package era.mi.gui.model.wires;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import era.mi.gui.model.components.GUIComponent;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class Pin
+{
+       public final GUIComponent component;
+
+       protected double relX;
+       protected double relY;
+
+       private final List<Consumer<? super Pin>> pinMovedListeners;
+
+       public Pin(GUIComponent component, double relX, double relY)
+       {
+               this.component = component;
+               this.relX = relX;
+               this.relY = relY;
+
+               this.pinMovedListeners = new ArrayList<>();
+
+               component.addComponentMovedListener(c -> callPinMovedListeners());
+       }
+
+       public double getRelX()
+       {
+               return relX;
+       }
+
+       public double getRelY()
+       {
+               return relY;
+       }
+
+       public Point getRelPos()
+       {
+               return new Point(relX, relY);
+       }
+
+       public Point getPos()
+       {
+               Rectangle componentBounds = component.getBounds();
+               return new Point(relX + componentBounds.x, relY + componentBounds.y);
+       }
+
+       // @formatter:off
+       public void addPinMovedListener   (Consumer<? super Pin> listener){pinMovedListeners.add   (listener);}
+
+       public void removePinMovedListener(Consumer<? super Pin> listener){pinMovedListeners.remove(listener);}
+
+       private void callPinMovedListeners() {pinMovedListeners.forEach(l -> l.accept(this));}
+       // @formatter:on
+
+       protected void setRelPos(double relX, double relY)
+       {
+               this.relX = relX;
+               this.relY = relY;
+               callPinMovedListeners();
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java b/LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java
new file mode 100644 (file)
index 0000000..ebe30c3
--- /dev/null
@@ -0,0 +1,33 @@
+package era.mi.gui.model.wires;
+
+import era.mi.gui.ColorHelper;
+import era.mi.gui.model.ViewModel;
+import era.mi.gui.model.components.GUIComponent;
+import era.mi.logic.types.BitVectorFormatter;
+import era.mi.logic.wires.Wire.ReadEnd;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class WireCrossPoint extends GUIComponent
+{
+       private ReadEnd end;
+
+       public WireCrossPoint(ViewModel model)
+       {
+               super(model);
+               setSize(0, 0);
+               addPin(new Pin(this, 0, 0));
+       }
+
+       @Override
+       public void render(GeneralGC gc, Rectangle visibleRegion)
+       {
+               ColorHelper.executeWithDifferentBackground(gc, BitVectorFormatter.formatAsColor(end), () -> gc.fillOval(-1, -1, 2, 2));
+       }
+
+       public void setLogicModelBinding(ReadEnd end)
+       {
+               this.end = end;
+               end.addObserver((i, o) -> callComponentChangedListeners());
+       }
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java b/LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java
new file mode 100644 (file)
index 0000000..f005a80
--- /dev/null
@@ -0,0 +1,6 @@
+package era.mi.gui.modeladapter;
+
+public class ViewLogicModelAdapter
+{
+
+}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/wires/GUIWire.java b/LogicUI/src/era/mi/gui/wires/GUIWire.java
deleted file mode 100644 (file)
index 31285bb..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-package era.mi.gui.wires;\r
-\r
-import java.util.Objects;\r
-\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.graphics.Color;\r
-\r
-import era.mi.gui.components.BasicGUIComponent;\r
-import era.mi.logic.types.Bit;\r
-import era.mi.logic.wires.Wire;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-\r
-public class GUIWire\r
-{\r
-       private final Wire wire;\r
-       private final double[] path;\r
-\r
-       public GUIWire(Runnable redraw, BasicGUIComponent component1, int component1ConnectionIndex, Point component1Pos,\r
-                       BasicGUIComponent component2, int component2ConnectionIndex, Point component2Pos, Point... path)\r
-       {\r
-               this.wire = component1.getConnectedWireEnd(component1ConnectionIndex).getWire();\r
-               if (!Objects.equals(wire, component2.getConnectedWireEnd(component2ConnectionIndex).getWire()))\r
-                       throw new IllegalArgumentException("Given connection points are not connected!");\r
-               this.path = new double[path.length * 2 + 4];\r
-               Point component1ConnectionPoint = component1.getWireEndConnectionPoint(component1ConnectionIndex);\r
-               this.path[0] = component1Pos.x + component1ConnectionPoint.x;\r
-               this.path[1] = component1Pos.y + component1ConnectionPoint.y;\r
-               for (int srcI = 0, dstI = 2; srcI < path.length; srcI++, dstI += 2)\r
-               {\r
-                       this.path[dstI + 0] = path[srcI].x;\r
-                       this.path[dstI + 1] = path[srcI].y;\r
-               }\r
-               Point component2ConnectionPoint = component2.getWireEndConnectionPoint(component2ConnectionIndex);\r
-               this.path[this.path.length - 2] = component2Pos.x + component2ConnectionPoint.x;\r
-               this.path[this.path.length - 1] = component2Pos.y + component2ConnectionPoint.y;\r
-\r
-               wire.createReadOnlyEnd().addObserver((initiator, oldValues) -> redraw.run());\r
-       }\r
-\r
-       public void render(GeneralGC gc)\r
-       {\r
-               Color oldFG = gc.getForeground();\r
-               if (wire.length == 1)\r
-                       gc.setForeground(gc.getDevice().getSystemColor(getSWTColorConstantForBit(wire.getValue())));\r
-               gc.drawPolyline(path);\r
-               gc.setForeground(oldFG);\r
-       }\r
-\r
-       public static int getSWTColorConstantForBit(Bit bit)\r
-       {\r
-               switch (bit)\r
-               {\r
-               case ONE:\r
-                       return SWT.COLOR_GREEN;\r
-               case ZERO:\r
-                       return SWT.COLOR_BLUE;\r
-               case Z:\r
-                       return SWT.COLOR_BLACK;\r
-               case U:\r
-               case X:\r
-                       return SWT.COLOR_RED;\r
-               default:\r
-                       throw new IllegalArgumentException("Unknown enum constant: " + bit);\r
-               }\r
-       }\r
-}
\ No newline at end of file
diff --git a/LogicUI/src/era/mi/gui/wires/WireConnectionPoint.java b/LogicUI/src/era/mi/gui/wires/WireConnectionPoint.java
deleted file mode 100644 (file)
index 3ccc1b4..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-package era.mi.gui.wires;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.List;\r
-\r
-import org.eclipse.swt.graphics.Color;\r
-\r
-import era.mi.gui.components.BasicGUIComponent;\r
-import era.mi.logic.wires.Wire;\r
-import era.mi.logic.wires.Wire.ReadEnd;\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-\r
-public class WireConnectionPoint implements BasicGUIComponent\r
-{\r
-       private final Wire wire;\r
-       private final List<ReadEnd> wireEnds;\r
-       private final int wiresCrossing;\r
-\r
-       public WireConnectionPoint(Wire wire, int wiresCrossing)\r
-       {\r
-               this.wire = wire;\r
-               List<ReadEnd> wireEndsModifiable = new ArrayList<>();\r
-               for (int i = 0; i < wiresCrossing; i++)\r
-                       wireEndsModifiable.add(wire.createReadOnlyEnd());\r
-               wireEnds = Collections.unmodifiableList(wireEndsModifiable);\r
-               this.wiresCrossing = wiresCrossing;\r
-       }\r
-\r
-       @Override\r
-       public void render(GeneralGC gc)\r
-       {\r
-               Color oldBG = gc.getBackground();\r
-               if (wire.length == 1)\r
-                       gc.setBackground(gc.getDevice().getSystemColor(GUIWire.getSWTColorConstantForBit(wire.getValue())));\r
-               gc.fillOval(-1, -1, 2, 2);\r
-               gc.setBackground(oldBG);\r
-       }\r
-\r
-       @Override\r
-       public Rectangle getBounds()\r
-       {\r
-               return new Rectangle(0, 0, 0, 0);\r
-       }\r
-\r
-       @Override\r
-       public int getConnectedWireEndsCount()\r
-       {\r
-               return wiresCrossing;\r
-       }\r
-\r
-       @Override\r
-       public ReadEnd getConnectedWireEnd(int connectionIndex)\r
-       {\r
-               return wireEnds.get(connectionIndex);\r
-       }\r
-\r
-       @Override\r
-       public Point getWireEndConnectionPoint(int connectionIndex)\r
-       {\r
-               return new Point(0, 0);\r
-       }\r
-}
\ No newline at end of file
index ee1d775..fa18e2d 100644 (file)
@@ -8,8 +8,7 @@ import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Composite;
 
 import era.mi.gui.LogicUICanvas;
-import era.mi.gui.examples.RSLatchGUIExample;
-import era.mi.logic.timeline.Timeline;
+import era.mi.gui.model.ViewModel;
 import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
 
 public class LogicUIPart
@@ -20,41 +19,41 @@ public class LogicUIPart
        @PostConstruct
        public void create(Composite parent)
        {
-               Timeline timeline = new Timeline(11);
-               LogicUICanvas ui = new LogicUICanvas(parent, SWT.NONE);
-               RSLatchGUIExample.addComponentsAndWires(ui, timeline);
+               ViewModel model = new ViewModel();
+               LogicUICanvas ui = new LogicUICanvas(parent, SWT.NONE, model);
+//             RSLatchGUIExample.addComponentsAndWires(ui, timeline);
                ui.addTransformListener((x, y, z) -> part.setDirty(z < 1));
                ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(ui);
                userInput.buttonDrag = 3;
                userInput.buttonZoom = 2;
                userInput.enableUserInput();
-               Thread simulationThread = new Thread(() ->
-               {
-                       // TODO find a better condition
-                       while (!ui.isDisposed())
-                       {
-                               // always execute to keep timeline from "hanging behind" for too long
-                               timeline.executeUntil(timeline.laterThan(System.currentTimeMillis()), System.currentTimeMillis() + 10);
-                               long sleepTime;
-                               if (timeline.hasNext())
-                                       sleepTime = timeline.nextEventTime() - System.currentTimeMillis();
-                               else
-                                       sleepTime = 10;
-                               try
-                               {
-                                       if (sleepTime > 0)
-                                               Thread.sleep(sleepTime);
-                               }
-                               catch (InterruptedException e)
-                               {
-                               } // it is normal execution flow to be interrupted
-                       }
-               });
-               simulationThread.start();
-               timeline.addEventAddedListener(event ->
-               {
-                       if (event.getTiming() <= System.currentTimeMillis())
-                               simulationThread.interrupt();
-               });
+//             Thread simulationThread = new Thread(() ->
+//             {
+//                     // TODO find a better condition
+//                     while (!ui.isDisposed())
+//                     {
+//                             // always execute to keep timeline from "hanging behind" for too long
+//                             timeline.executeUpTo(System.currentTimeMillis(), System.currentTimeMillis() + 10);
+//                             long sleepTime;
+//                             if (timeline.hasNext())
+//                                     sleepTime = timeline.nextEventTime() - System.currentTimeMillis();
+//                             else
+//                                     sleepTime = 10;
+//                             try
+//                             {
+//                                     if (sleepTime > 0)
+//                                             Thread.sleep(sleepTime);
+//                             }
+//                             catch (InterruptedException e)
+//                             {
+//                             } // it is normal execution flow to be interrupted
+//                     }
+//             });
+//             simulationThread.start();
+//             timeline.addEventAddedListener(event ->
+//             {
+//                     if (event.getTiming() <= System.currentTimeMillis())
+//                             simulationThread.interrupt();
+//             });
        }
 }
\ No newline at end of file
diff --git a/era.mi/src/era/mi/logic/types/BitVectorFormatter.java b/era.mi/src/era/mi/logic/types/BitVectorFormatter.java
new file mode 100644 (file)
index 0000000..6dab1d7
--- /dev/null
@@ -0,0 +1,53 @@
+package era.mi.logic.types;
+
+import era.mi.logic.types.ColorDefinition.BuiltInColor;
+import era.mi.logic.wires.Wire.ReadEnd;
+
+public class BitVectorFormatter
+{
+       public static String formatValueAsString(ReadEnd end)
+       {
+               return formatAsString(end == null ? null : end.getValues());
+       }
+
+       public static String formatAsString(BitVector bitVector)
+       {
+               if (bitVector == null)
+                       return "null";
+               else
+                       return bitVector.toString();
+       }
+
+       public static ColorDefinition formatAsColor(ReadEnd end)
+       {
+               return formatAsColor(end == null ? null : end.getValues());
+       }
+
+       public static ColorDefinition formatAsColor(BitVector bitVector)
+       {
+               // TODO maybe find a color assignment for multiple-bit bit vectors?
+               if (bitVector == null || bitVector.length() != 1)
+                       return new ColorDefinition(BuiltInColor.COLOR_BLACK);
+               else
+                       switch (bitVector.getBit(0))
+                       {
+                       case ONE:
+                               return new ColorDefinition(BuiltInColor.COLOR_GREEN);
+                       case U:
+                               return new ColorDefinition(BuiltInColor.COLOR_CYAN);
+                       case X:
+                               return new ColorDefinition(BuiltInColor.COLOR_RED);
+                       case Z:
+                               return new ColorDefinition(BuiltInColor.COLOR_YELLOW);
+                       case ZERO:
+                               return new ColorDefinition(BuiltInColor.COLOR_GRAY);
+                       default:
+                               throw new IllegalArgumentException("Unknown enum constant: " + bitVector.getBit(0));
+                       }
+       }
+
+       private BitVectorFormatter()
+       {
+               throw new UnsupportedOperationException("No BitVectorFormatter instances");
+       }
+}
\ No newline at end of file
diff --git a/era.mi/src/era/mi/logic/types/ColorDefinition.java b/era.mi/src/era/mi/logic/types/ColorDefinition.java
new file mode 100644 (file)
index 0000000..b9e851f
--- /dev/null
@@ -0,0 +1,54 @@
+package era.mi.logic.types;
+
+/**
+ * A way to define a color with the possibility to use colors built into the system (called "system colors" in SWT).
+ * <p>
+ * A {@link ColorDefinition} is defined either by a {@link BuiltInColor} constant, in which case <code>r==g==b==-1</code>, or by red / green
+ * / blue components, in which case <code>builtInColor==null</code>
+ */
+public class ColorDefinition
+{
+       /**
+        * The built-in color constant defining this color.
+        */
+       public final ColorDefinition.BuiltInColor builtInColor;
+       /**
+        * The red color component defining this color.
+        */
+       public final int r;
+       /**
+        * The green color component defining this color.
+        */
+       public final int g;
+       /**
+        * The blue color component defining this color.
+        */
+       public final int b;
+
+       public ColorDefinition(ColorDefinition.BuiltInColor col)
+       {
+               if (col == null)
+                       throw new IllegalArgumentException("Illegal built-in color: " + col);
+               this.builtInColor = col;
+               this.r = -1;
+               this.g = -1;
+               this.b = -1;
+       }
+
+       public ColorDefinition(int r, int g, int b)
+       {
+               if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
+                       throw new IllegalArgumentException("Illegal color components: r=" + r + "; g=" + g + "; b=" + b);
+               this.builtInColor = null;
+               this.r = r;
+               this.g = g;
+               this.b = b;
+       }
+
+       public static enum BuiltInColor
+       {
+               COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_DARK_RED, COLOR_GREEN, COLOR_DARK_GREEN, COLOR_YELLOW, COLOR_DARK_YELLOW, COLOR_BLUE,
+               COLOR_DARK_BLUE, COLOR_MAGENTA, COLOR_DARK_MAGENTA, COLOR_CYAN, COLOR_DARK_CYAN, COLOR_GRAY, COLOR_DARK_GRAY;
+       }
+
+}
\ No newline at end of file