Fixed issues with WirePointHandle
[Mograsim.git] / net.mograsim.logic.model / src / net / mograsim / logic / model / model / wires / GUIWire.java
index c1070e5..144a1a3 100644 (file)
@@ -3,6 +3,7 @@ package net.mograsim.logic.model.model.wires;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.function.Consumer;
 
 import org.eclipse.swt.SWT;
 
@@ -29,6 +30,10 @@ public class GUIWire
         * The model this wire is a part of.
         */
        private final ViewModelModifiable model;
+       /**
+        * The name of this wire. Is unique for all wires in its model.
+        */
+       public final String name;
        /**
         * The logical width of this wire. Is equal to the logical with of {@link #pin1} and {@link #pin2}.
         */
@@ -57,6 +62,7 @@ public class GUIWire
        private double[] effectivePath;
 
        private final List<Runnable> redrawListeners;
+       private final List<Consumer<GUIWire>> pathChangedListeners;
 
        /**
         * A LogicObserver calling redrawListeners. Used for logic model bindings.
@@ -70,84 +76,164 @@ public class GUIWire
        // creation and destruction
 
        /**
-        * Creates a new {@link GUIWire} with automatic interpolation.
+        * Creates a new {@link GUIWire} with automatic interpolation and using the default name.
         * 
         * @author Daniel Kirschten
         */
        public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, WireCrossPoint pin2)
        {
-               this(model, pin1, pin2, (Point[]) null);
+               this(model, null, pin1, pin2);
        }
 
        /**
-        * Creates a new {@link GUIWire} with automatic interpolation.
+        * Creates a new {@link GUIWire} with automatic interpolation and using the default name.
         * 
         * @author Daniel Kirschten
         */
        public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, Pin pin2)
        {
-               this(model, pin1, pin2, (Point[]) null);
+               this(model, null, pin1, pin2);
        }
 
        /**
-        * Creates a new {@link GUIWire} with automatic interpolation.
+        * Creates a new {@link GUIWire} with automatic interpolation and using the default name.
         * 
         * @author Daniel Kirschten
         */
        public GUIWire(ViewModelModifiable model, Pin pin1, WireCrossPoint pin2)
        {
-               this(model, pin1, pin2, (Point[]) null);
+               this(model, null, pin1, pin2);
        }
 
        /**
-        * Creates a new {@link GUIWire} with automatic interpolation.
+        * Creates a new {@link GUIWire} with automatic interpolation and using the default name.
         * 
         * @author Daniel Kirschten
         */
        public GUIWire(ViewModelModifiable model, Pin pin1, Pin pin2)
        {
-               this(model, pin1, pin2, (Point[]) null);
+               this(model, null, pin1, pin2);
        }
 
        /**
-        * Creates a new {@link GUIWire} without automatic interpolation.
+        * Creates a new {@link GUIWire} without automatic interpolation and using the default name.
         * 
         * @author Daniel Kirschten
         */
        public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, WireCrossPoint pin2, Point... path)
        {
-               this(model, pin1.getPin(), pin2.getPin(), path);
+               this(model, null, pin1, pin2, path);
        }
 
        /**
-        * Creates a new {@link GUIWire} without automatic interpolation.
+        * Creates a new {@link GUIWire} without automatic interpolation and using the default name.
         * 
         * @author Daniel Kirschten
         */
        public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, Pin pin2, Point... path)
        {
-               this(model, pin1.getPin(), pin2, path);
+               this(model, null, pin1, pin2, path);
        }
 
        /**
-        * Creates a new {@link GUIWire} without automatic interpolation.
+        * Creates a new {@link GUIWire} without automatic interpolation and using the default name.
         * 
         * @author Daniel Kirschten
         */
        public GUIWire(ViewModelModifiable model, Pin pin1, WireCrossPoint pin2, Point... path)
        {
-               this(model, pin1, pin2.getPin(), path);
+               this(model, null, pin1, pin2, path);
        }
 
        /**
-        * Creates a new {@link GUIWire} without automatic interpolation.
+        * Creates a new {@link GUIWire} without automatic interpolation and using the default name.
         * 
         * @author Daniel Kirschten
         */
        public GUIWire(ViewModelModifiable model, Pin pin1, Pin pin2, Point... path)
        {
-               logicObs = (i) -> callRedrawListeners();
+               this(model, null, pin1, pin2, path);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} with automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, String name, WireCrossPoint pin1, WireCrossPoint pin2)
+       {
+               this(model, name, pin1, pin2, (Point[]) null);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} with automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, String name, WireCrossPoint pin1, Pin pin2)
+       {
+               this(model, name, pin1, pin2, (Point[]) null);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} with automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, String name, Pin pin1, WireCrossPoint pin2)
+       {
+               this(model, name, pin1, pin2, (Point[]) null);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} with automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, String name, Pin pin1, Pin pin2)
+       {
+               this(model, name, pin1, pin2, (Point[]) null);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} without automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, String name, WireCrossPoint pin1, WireCrossPoint pin2, Point... path)
+       {
+               this(model, name, pin1.getPin(), pin2.getPin(), path);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} without automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, String name, WireCrossPoint pin1, Pin pin2, Point... path)
+       {
+               this(model, name, pin1.getPin(), pin2, path);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} without automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, String name, Pin pin1, WireCrossPoint pin2, Point... path)
+       {
+               this(model, name, pin1, pin2.getPin(), path);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} without automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, String name, Pin pin1, Pin pin2, Point... path)
+       {
                this.model = model;
+               this.name = name == null ? model.getDefaultWireName() : name;
                this.logicWidth = pin1.logicWidth;
                if (pin2.logicWidth != pin1.logicWidth)
                        throw new IllegalArgumentException("Can't connect pins of different logic width");
@@ -159,6 +245,9 @@ public class GUIWire
                this.bounds = new Rectangle(0, 0, -1, -1);
 
                redrawListeners = new ArrayList<>();
+               pathChangedListeners = new ArrayList<>();
+
+               logicObs = (i) -> callRedrawListeners();
 
                pin1.addPinMovedListener(p -> pinMoved());
                pin2.addPinMovedListener(p -> pinMoved());
@@ -283,6 +372,8 @@ public class GUIWire
                gc.drawPolyline(effectivePath);
        }
 
+       // operations concerning the path
+
        /**
         * The user-defined path between {@link #pin1} and {@link #pin2}. Note that this is not neccessarily equal to the effective path drawn
         * in {@link #render(GeneralGC)}.<br>
@@ -293,7 +384,81 @@ public class GUIWire
         */
        public Point[] getPath()
        {
-               return path == null ? null : path.clone();
+               return deepPathCopy(path);
+       }
+
+       public void setPath(Point... path)
+       {
+               this.path = deepPathCopy(path);
+               recalculateEffectivePath();
+               callPathChangedListeners();
+               callRedrawListeners();
+       }
+
+       public Point getPathPoint(int index)
+       {
+               return pointCopy(path[index]);
+       }
+
+       public void setPathPoint(Point p, int index)
+       {
+               path[index] = pointCopy(p);
+               recalculateEffectivePath();
+               callPathChangedListeners();
+               callRedrawListeners();
+       }
+
+       public void insertPathPoint(Point p, int index)
+       {
+               if (path == null)
+                       path = new Point[] { pointCopy(p) };
+               else
+               {
+                       Point[] oldPath = path;
+                       path = new Point[oldPath.length + 1];
+                       System.arraycopy(oldPath, 0, path, 0, index);
+                       if (index < oldPath.length)
+                               System.arraycopy(oldPath, index, path, index + 1, oldPath.length - index);
+                       path[index] = pointCopy(p);
+               }
+               recalculateEffectivePath();
+               callPathChangedListeners();
+       }
+
+       public void removePathPoint(int index)
+       {
+               if (path.length == 0)
+                       path = null;
+               else
+               {
+                       Point[] oldPath = path;
+                       path = new Point[oldPath.length - 1];
+                       System.arraycopy(oldPath, 0, path, 0, index);
+                       if (index < oldPath.length - 1)
+                               System.arraycopy(oldPath, index + 1, path, index, oldPath.length - index - 1);
+               }
+               recalculateEffectivePath();
+               callPathChangedListeners();
+       }
+
+       public double[] getEffectivePath()
+       {
+               return Arrays.copyOf(effectivePath, effectivePath.length);
+       }
+
+       private static Point[] deepPathCopy(Point[] path)
+       {
+               if (path == null)
+                       return null;
+               Point[] copy = new Point[path.length];
+               for (int i = 0; i < path.length; i++)
+                       copy[i] = pointCopy(path[i]);
+               return copy;
+       }
+
+       private static Point pointCopy(Point p)
+       {
+               return new Point(p.x, p.y);
        }
 
        // logic model binding
@@ -349,11 +514,14 @@ public class GUIWire
        // listeners
 
        // @formatter:off
-       public void addRedrawListener   (Runnable listener) {redrawListeners         .add   (listener);}
+       public void addRedrawListener        (Runnable          listener) {redrawListeners     .add    (listener);}
+       public void addPathChangedListener   (Consumer<GUIWire> listener) {pathChangedListeners.add    (listener);}
 
-       public void removeRedrawListener(Runnable listener) {redrawListeners         .remove(listener);}
+       public void removeRedrawListener     (Runnable          listener) {redrawListeners     .remove(listener);}
+       public void removePathChangedListener(Consumer<GUIWire> listener) {pathChangedListeners.remove(listener);}
 
-       private void callRedrawListeners() {redrawListeners.forEach(l -> l.run());}
+       private void callRedrawListeners     () {redrawListeners     .forEach(l -> l.run   (    ));}
+       private void callPathChangedListeners() {pathChangedListeners.forEach(l -> l.accept(this));}
        // @formatter:on
 
        @Override