diff --git a/diylc/diylc-core/src/main/java/org/diylc/common/PropertyWrapper.java b/diylc/diylc-core/src/main/java/org/diylc/common/PropertyWrapper.java
index 3232d432..b6184677 100644
--- a/diylc/diylc-core/src/main/java/org/diylc/common/PropertyWrapper.java
+++ b/diylc/diylc-core/src/main/java/org/diylc/common/PropertyWrapper.java
@@ -24,6 +24,7 @@ package org.diylc.common;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import org.diylc.core.IDynamicPropertySource;
import org.diylc.core.IPropertyValidator;
/**
@@ -45,9 +46,10 @@ public class PropertyWrapper implements Cloneable {
private boolean changed = false;
private int sortOrder;
private Object ownerObject;
+ private IDynamicPropertySource dynamicPropertySource;
public PropertyWrapper(String name, Class> type, String getter, String setter, boolean defaultable,
- IPropertyValidator validator, int sortOrder) {
+ IPropertyValidator validator, int sortOrder, IDynamicPropertySource dynamicPropertySource) {
super();
this.name = name;
this.type = type;
@@ -56,6 +58,7 @@ public class PropertyWrapper implements Cloneable {
this.defaultable = defaultable;
this.validator = validator;
this.sortOrder = sortOrder;
+ this.dynamicPropertySource = dynamicPropertySource;
this.ownerObject = null;
}
@@ -138,6 +141,10 @@ public class PropertyWrapper implements Cloneable {
return sortOrder;
}
+ public IDynamicPropertySource getDynamicPropertySource() {
+ return dynamicPropertySource;
+ }
+
// @Override
// public Object clone() throws CloneNotSupportedException {
// // Try to invoke clone method on value if possible.
@@ -152,7 +159,7 @@ public class PropertyWrapper implements Cloneable {
public Object clone() throws CloneNotSupportedException {
PropertyWrapper clone =
new PropertyWrapper(this.name, this.type, this.getter, this.setter, this.defaultable, this.validator,
- this.sortOrder);
+ this.sortOrder, this.dynamicPropertySource);
clone.value = this.value;
clone.changed = this.changed;
clone.unique = this.unique;
diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/IDynamicPropertySource.java b/diylc/diylc-core/src/main/java/org/diylc/core/IDynamicPropertySource.java
new file mode 100644
index 00000000..1f48d97c
--- /dev/null
+++ b/diylc/diylc-core/src/main/java/org/diylc/core/IDynamicPropertySource.java
@@ -0,0 +1,34 @@
+/*
+
+ DIY Layout Creator (DIYLC).
+ Copyright (c) 2009-2018 held jointly by the individual authors.
+
+ This file is part of DIYLC.
+
+ DIYLC is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DIYLC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DIYLC. If not, see .
+
+*/
+package org.diylc.core;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IDynamicPropertySource {
+
+ void setComponent(IDIYComponent> component);
+
+ List getAvailableValues();
+
+ String getDisplayValue(Object value);
+}
diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/ISwitch.java b/diylc/diylc-core/src/main/java/org/diylc/core/ISwitch.java
index 7c826c70..994621e0 100644
--- a/diylc/diylc-core/src/main/java/org/diylc/core/ISwitch.java
+++ b/diylc/diylc-core/src/main/java/org/diylc/core/ISwitch.java
@@ -21,6 +21,8 @@
*/
package org.diylc.core;
+import java.awt.*;
+
/**
* Interface for all component that can act as switches. They must have at least one position.
*
@@ -28,6 +30,13 @@ package org.diylc.core;
*/
public interface ISwitch {
+ Color[] POLE_COLORS = new Color[] { Color.decode("#FFD1DC"),
+ Color.decode("#FFE5B4"),
+ Color.decode("#FFFACD"),
+ Color.decode("#B8F2E6"),
+ Color.decode("#AEC6CF"),
+ Color.decode("#E6E6FA")};
+
/**
* @return a positive number of positions.
*/
@@ -48,4 +57,15 @@ public interface ISwitch {
* @return true if the two points are connected in the given switch position, false otherwise.
*/
boolean arePointsConnected(int index1, int index2, int position);
+
+ /**
+ * @return currently selected switch position, or null if none
+ */
+ default Integer getSelectedPosition() {
+ return null;
+ }
+
+ default Boolean getRenderConnectedTerminals() {
+ return false;
+ }
}
diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/SwitchPositionPropertyValueSource.java b/diylc/diylc-core/src/main/java/org/diylc/core/SwitchPositionPropertyValueSource.java
new file mode 100644
index 00000000..ff94f52f
--- /dev/null
+++ b/diylc/diylc-core/src/main/java/org/diylc/core/SwitchPositionPropertyValueSource.java
@@ -0,0 +1,38 @@
+package org.diylc.core;
+
+import java.util.*;
+
+public class SwitchPositionPropertyValueSource implements IDynamicPropertySource {
+
+ private IDIYComponent> component;
+
+ @Override
+ public void setComponent(IDIYComponent> component) {
+ this.component = component;
+ }
+
+ @Override
+ public List getAvailableValues() {
+ if (component == null) {
+ throw new RuntimeException("Component not set");
+ }
+ if (!(component instanceof ISwitch sw)) {
+ return List.of();
+ }
+ int positionCount = sw.getPositionCount();
+ List res = new ArrayList<>();
+ res.add(null);
+ for (int i = 0; i < positionCount; i++) {
+ res.add(i);
+ }
+ return res;
+ }
+
+ @Override
+ public String getDisplayValue(Object value) {
+ if (value == null) {
+ return "";
+ }
+ return "#" + (((Integer)value) + 1);
+ }
+}
diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/annotations/DynamicEditableProperty.java b/diylc/diylc-core/src/main/java/org/diylc/core/annotations/DynamicEditableProperty.java
new file mode 100644
index 00000000..44f326c8
--- /dev/null
+++ b/diylc/diylc-core/src/main/java/org/diylc/core/annotations/DynamicEditableProperty.java
@@ -0,0 +1,42 @@
+/*
+
+ DIY Layout Creator (DIYLC).
+ Copyright (c) 2009-2018 held jointly by the individual authors.
+
+ This file is part of DIYLC.
+
+ DIYLC is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DIYLC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DIYLC. If not, see .
+
+*/
+package org.diylc.core.annotations;
+
+import org.diylc.core.IDynamicPropertySource;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/***
+ * Used for properties having dynamic set of possible values. Only to be used in addition to
+ * EditableProperty annotation.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DynamicEditableProperty {
+
+ /**
+ * Optional parameter for dynamic values
+ *
+ * @return
+ */
+ Class extends IDynamicPropertySource> source() ;
+}
diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/annotations/EditableProperty.java b/diylc/diylc-core/src/main/java/org/diylc/core/annotations/EditableProperty.java
index ba31063a..575b2a77 100644
--- a/diylc/diylc-core/src/main/java/org/diylc/core/annotations/EditableProperty.java
+++ b/diylc/diylc-core/src/main/java/org/diylc/core/annotations/EditableProperty.java
@@ -25,6 +25,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.diylc.core.AllowAllValidator;
+import org.diylc.core.IDynamicPropertySource;
import org.diylc.core.IPropertyValidator;
/**
diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java
index 3b839c92..7a865ddf 100644
--- a/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java
+++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java
@@ -41,11 +41,9 @@ import org.diylc.common.IComponentTransformer;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDynamicPropertySource;
import org.diylc.core.IPropertyValidator;
-import org.diylc.core.annotations.BomPolicy;
-import org.diylc.core.annotations.ComponentDescriptor;
-import org.diylc.core.annotations.EditableProperty;
-import org.diylc.core.annotations.KeywordPolicy;
+import org.diylc.core.annotations.*;
import org.diylc.lang.LangUtil;
/**
@@ -189,10 +187,16 @@ public class ComponentProcessor {
LangUtil.translate(o.toString());
}
}
+
+ IDynamicPropertySource dynamicPropertySource = null;
+ if (getter.isAnnotationPresent(DynamicEditableProperty.class)) {
+ DynamicEditableProperty dynamicAnnotation = getter.getAnnotation(DynamicEditableProperty.class);
+ dynamicPropertySource = getDynamicPropertySource(dynamicAnnotation.source());
+ }
PropertyWrapper property =
new PropertyWrapper(name, getter.getReturnType(), getter.getName(), setterName,
- annotation.defaultable(), validator, annotation.sortOrder());
+ annotation.defaultable(), validator, annotation.sortOrder(), dynamicPropertySource);
properties.add(property);
}
}
@@ -278,6 +282,17 @@ public class ComponentProcessor {
return validator;
}
+ private IDynamicPropertySource getDynamicPropertySource(Class extends IDynamicPropertySource> clazz) {
+ IDynamicPropertySource source;
+ try {
+ source = clazz.getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ LOG.error("Could not instantiate source for " + clazz.getName(), e);
+ return null;
+ }
+ return source;
+ }
+
private IComponentTransformer getComponentTransformer(Class extends IComponentTransformer> clazz) {
if (clazz == null)
return null;
diff --git a/diylc/diylc-core/src/main/java/org/diylc/utils/ResourceUtils.java b/diylc/diylc-core/src/main/java/org/diylc/utils/ResourceUtils.java
index 9a638584..6a1cbb25 100644
--- a/diylc/diylc-core/src/main/java/org/diylc/utils/ResourceUtils.java
+++ b/diylc/diylc-core/src/main/java/org/diylc/utils/ResourceUtils.java
@@ -1,3 +1,24 @@
+/*
+
+ DIY Layout Creator (DIYLC).
+ Copyright (c) 2009-2018 held jointly by the individual authors.
+
+ This file is part of DIYLC.
+
+ DIYLC is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DIYLC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DIYLC. If not, see .
+
+*/
package org.diylc.utils;
import org.apache.log4j.Logger;
diff --git a/diylc/diylc-core/src/main/java/org/diylc/utils/ScaledBufferedImage.java b/diylc/diylc-core/src/main/java/org/diylc/utils/ScaledBufferedImage.java
index 78b9db3b..d7b102e5 100644
--- a/diylc/diylc-core/src/main/java/org/diylc/utils/ScaledBufferedImage.java
+++ b/diylc/diylc-core/src/main/java/org/diylc/utils/ScaledBufferedImage.java
@@ -1,3 +1,24 @@
+/*
+
+ DIY Layout Creator (DIYLC).
+ Copyright (c) 2009-2018 held jointly by the individual authors.
+
+ This file is part of DIYLC.
+
+ DIYLC is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DIYLC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DIYLC. If not, see .
+
+*/
package org.diylc.utils;
import java.awt.Graphics2D;
diff --git a/diylc/diylc-core/src/main/java/org/diylc/utils/SwitchUtils.java b/diylc/diylc-core/src/main/java/org/diylc/utils/SwitchUtils.java
new file mode 100644
index 00000000..40bac993
--- /dev/null
+++ b/diylc/diylc-core/src/main/java/org/diylc/utils/SwitchUtils.java
@@ -0,0 +1,89 @@
+/*
+
+ DIY Layout Creator (DIYLC).
+ Copyright (c) 2009-2018 held jointly by the individual authors.
+
+ This file is part of DIYLC.
+
+ DIYLC is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DIYLC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DIYLC. If not, see .
+
+*/
+package org.diylc.utils;
+
+import org.diylc.core.ISwitch;
+
+import java.util.*;
+
+public class SwitchUtils {
+
+ public static List> getConnectedTerminals(ISwitch switchComponent, int numPoints) {
+ List> connectedGroups = new ArrayList<>();
+
+ // Only process if we have a selected position and should render connections
+ if (switchComponent.getSelectedPosition() == null || !Boolean.TRUE.equals(switchComponent.getRenderConnectedTerminals())) {
+ return connectedGroups;
+ }
+
+ // First, collect all direct connections
+ List connections = new ArrayList<>();
+ for (int i = 0; i < numPoints; i++) {
+ for (int j = i + 1; j < numPoints; j++) {
+ if (switchComponent.arePointsConnected(i, j, switchComponent.getSelectedPosition())) {
+ connections.add(new int[]{i, j});
+ }
+ }
+ }
+
+ // Create a map to track which points are in which group
+ Map> pointToGroup = new HashMap<>();
+
+ // Process each connection
+ for (int[] connection : connections) {
+ int p1 = connection[0];
+ int p2 = connection[1];
+
+ Set group1 = pointToGroup.get(p1);
+ Set group2 = pointToGroup.get(p2);
+
+ if (group1 == null && group2 == null) {
+ // Create new group
+ Set newGroup = new HashSet<>();
+ newGroup.add(p1);
+ newGroup.add(p2);
+ pointToGroup.put(p1, newGroup);
+ pointToGroup.put(p2, newGroup);
+ } else if (group1 == null) {
+ // Add p1 to p2's group
+ group2.add(p1);
+ pointToGroup.put(p1, group2);
+ } else if (group2 == null) {
+ // Add p2 to p1's group
+ group1.add(p2);
+ pointToGroup.put(p2, group1);
+ } else if (group1 != group2) {
+ // Merge groups
+ group1.addAll(group2);
+ for (Integer p : group2) {
+ pointToGroup.put(p, group1);
+ }
+ }
+ }
+
+ // Collect unique groups
+ Set> uniqueGroups = new HashSet<>(pointToGroup.values());
+ connectedGroups.addAll(uniqueGroups);
+
+ return connectedGroups;
+ }
+}
diff --git a/diylc/diylc-core/src/test/java/org/diylc/utils/SwitchUtilsTest.java b/diylc/diylc-core/src/test/java/org/diylc/utils/SwitchUtilsTest.java
new file mode 100644
index 00000000..878289e0
--- /dev/null
+++ b/diylc/diylc-core/src/test/java/org/diylc/utils/SwitchUtilsTest.java
@@ -0,0 +1,136 @@
+package org.diylc.utils;
+
+import org.diylc.core.ISwitch;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+public class SwitchUtilsTest {
+
+ // Mock switch implementation for testing
+ private static class MockSwitch implements ISwitch {
+ private final List connections;
+ private final Integer selectedPosition;
+ private final boolean renderConnected;
+
+ public MockSwitch(List connections, Integer selectedPosition, boolean renderConnected) {
+ this.connections = connections;
+ this.selectedPosition = selectedPosition;
+ this.renderConnected = renderConnected;
+ }
+
+ @Override
+ public boolean arePointsConnected(int index1, int index2, int position) {
+ if (position != selectedPosition) return false;
+ return connections.stream()
+ .anyMatch(conn -> (conn[0] == index1 && conn[1] == index2) ||
+ (conn[0] == index2 && conn[1] == index1));
+ }
+
+ @Override
+ public Integer getSelectedPosition() {
+ return selectedPosition;
+ }
+
+ @Override
+ public Boolean getRenderConnectedTerminals() {
+ return renderConnected;
+ }
+
+ // Other interface methods not needed for testing
+ @Override public int getPositionCount() { return 0; }
+ @Override public String getPositionName(int position) { return null; }
+ }
+
+ @Test
+ public void testNoConnections() {
+ MockSwitch mockSwitch = new MockSwitch(Collections.emptyList(), 0, true);
+ List> groups = SwitchUtils.getConnectedTerminals(mockSwitch, 5);
+ assertTrue("Should return empty list when no connections", groups.isEmpty());
+ }
+
+ @Test
+ public void testSingleConnection() {
+ List connections = Arrays.asList(new int[]{0, 1});
+ MockSwitch mockSwitch = new MockSwitch(connections, 0, true);
+ List> groups = SwitchUtils.getConnectedTerminals(mockSwitch, 5);
+
+ assertEquals("Should have one group", 1, groups.size());
+ assertTrue("Group should contain points 0 and 1",
+ groups.get(0).containsAll(Arrays.asList(0, 1)));
+ }
+
+ @Test
+ public void testTransitiveConnections() {
+ // Test case where 1-2-3 are connected through 2
+ List connections = Arrays.asList(
+ new int[]{1, 2},
+ new int[]{2, 3}
+ );
+ MockSwitch mockSwitch = new MockSwitch(connections, 0, true);
+ List> groups = SwitchUtils.getConnectedTerminals(mockSwitch, 5);
+
+ assertEquals("Should have one group", 1, groups.size());
+ assertTrue("Group should contain points 1, 2, and 3",
+ groups.get(0).containsAll(Arrays.asList(1, 2, 3)));
+ }
+
+ @Test
+ public void testMultipleGroups() {
+ // Test case with two separate groups: {1,2,3} and {4,6,7}
+ List connections = Arrays.asList(
+ new int[]{2, 3},
+ new int[]{1, 3},
+ new int[]{4, 7},
+ new int[]{4, 6}
+ );
+ MockSwitch mockSwitch = new MockSwitch(connections, 0, true);
+ List> groups = SwitchUtils.getConnectedTerminals(mockSwitch, 8);
+
+ assertEquals("Should have two groups", 2, groups.size());
+ assertTrue("Groups should contain {1,2,3} and {4,6,7}",
+ groups.stream().anyMatch(g -> g.containsAll(Arrays.asList(1, 2, 3))) &&
+ groups.stream().anyMatch(g -> g.containsAll(Arrays.asList(4, 6, 7))));
+ }
+
+ @Test
+ public void testRenderConnectedFalse() {
+ List connections = Arrays.asList(new int[]{0, 1});
+ MockSwitch mockSwitch = new MockSwitch(connections, 0, false);
+ List> groups = SwitchUtils.getConnectedTerminals(mockSwitch, 5);
+ assertTrue("Should return empty list when renderConnected is false", groups.isEmpty());
+ }
+
+ @Test
+ public void testNoSelectedPosition() {
+ List connections = Arrays.asList(new int[]{0, 1});
+ MockSwitch mockSwitch = new MockSwitch(connections, null, true);
+ List> groups = SwitchUtils.getConnectedTerminals(mockSwitch, 5);
+ assertTrue("Should return empty list when no position selected", groups.isEmpty());
+ }
+
+ @Test
+ public void testComplexConnections() {
+ // Test case with multiple interconnected points
+ List connections = Arrays.asList(
+ new int[]{0, 1},
+ new int[]{1, 2},
+ new int[]{2, 3},
+ new int[]{3, 0}, // Creates a circle
+ new int[]{4, 5},
+ new int[]{5, 6},
+ new int[]{7, 8} // Separate pair
+ );
+ MockSwitch mockSwitch = new MockSwitch(connections, 0, true);
+ List> groups = SwitchUtils.getConnectedTerminals(mockSwitch, 9);
+
+ assertEquals("Should have three groups", 3, groups.size());
+ assertTrue("Should have group with points 0,1,2,3",
+ groups.stream().anyMatch(g -> g.containsAll(Arrays.asList(0, 1, 2, 3))));
+ assertTrue("Should have group with points 4,5,6",
+ groups.stream().anyMatch(g -> g.containsAll(Arrays.asList(4, 5, 6))));
+ assertTrue("Should have group with points 7,8",
+ groups.stream().anyMatch(g -> g.containsAll(Arrays.asList(7, 8))));
+ }
+}
diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/guitar/LeverSwitch.java b/diylc/diylc-library/src/main/java/org/diylc/components/guitar/LeverSwitch.java
index 5834732b..168abc81 100644
--- a/diylc/diylc-library/src/main/java/org/diylc/components/guitar/LeverSwitch.java
+++ b/diylc/diylc-library/src/main/java/org/diylc/components/guitar/LeverSwitch.java
@@ -32,28 +32,26 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
-import java.util.Arrays;
-import java.util.List;
+import java.util.*;
+import java.util.stream.IntStream;
+
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
import org.diylc.components.AbstractAngledComponent;
import org.diylc.components.transform.AngledComponentTransformer;
-import org.diylc.core.ComponentState;
-import org.diylc.core.IDIYComponent;
-import org.diylc.core.IDrawingObserver;
-import org.diylc.core.ISwitch;
-import org.diylc.core.Project;
-import org.diylc.core.Theme;
-import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.*;
import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.DynamicEditableProperty;
import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
+import static org.diylc.utils.SwitchUtils.getConnectedTerminals;
+
@ComponentDescriptor(name = "Lever Switch", category = "Guitar", author = "Branislav Stojkovic",
description = "Strat-style lever switch", zOrder = IDIYComponent.COMPONENT,
instanceNamePrefix = "SW", keywordPolicy = KeywordPolicy.SHOW_TAG,
@@ -65,7 +63,6 @@ public class LeverSwitch extends AbstractAngledComponent> connectedTerminals = getConnectedTerminals(this, controlPoints.length);
+
+ body = new Shape[3 + connectedTerminals.size()];
+ body[0] = baseArea;
+ body[1] = waferArea;
+ for (int i = 0; i <= connectedTerminals.size(); i++) {
+ body[2 + i] = new Area();
+ }
+
for (int i = 0; i < controlPoints.length; i++) {
Point2D point = controlPoints[i];
Area terminal =
@@ -222,19 +229,14 @@ public class LeverSwitch extends AbstractAngledComponent connectedTerminals.get(j).contains(finalI))
+ .findFirst().orElse(-1);
+
+ ((Area)body[3 + groupIndex]).add(terminal);
}
- body[2] = terminalArea;
- body[3] = commonTerminalArea;
// Rotate if needed
if (theta != 0) {
@@ -250,6 +252,8 @@ public class LeverSwitch extends AbstractAngledComponent .
+
+*/
+package org.diylc.swing.gui.editor;
+
+import org.diylc.common.PropertyWrapper;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDynamicPropertySource;
+import org.diylc.lang.LangUtil;
+import org.diylc.utils.Constants;
+
+import javax.swing.*;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import java.awt.*;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseListener;
+import java.util.Vector;
+
+public class DynamicValueEditor extends JComboBox {
+
+ private static final long serialVersionUID = 1L;
+
+ private Color oldBg = getBackground();
+
+ private final PropertyWrapper property;
+
+ public DynamicValueEditor(final PropertyWrapper property) {
+ this.property = property;
+ IDynamicPropertySource dynamicPropertySource = property.getDynamicPropertySource();
+ if (property.getOwnerObject() == null || !(property.getOwnerObject() instanceof IDIYComponent)) {
+ setModel(new DefaultComboBoxModel(new Object[0]));
+ setEnabled(false);
+ } else {
+ dynamicPropertySource.setComponent((IDIYComponent>) property.getOwnerObject());
+ setModel(new DynamicComboBoxModel(dynamicPropertySource));
+ }
+ if (property.isReadOnly())
+ setEnabled(false);
+
+ setRenderer(new DefaultListCellRenderer() {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Component getListCellRendererComponent(JList> list, Object value, int index,
+ boolean isSelected, boolean cellHasFocus) {
+ // TODO Auto-generated method stub
+ return super.getListCellRendererComponent(list, value == null ? null : LangUtil.translate(dynamicPropertySource.getDisplayValue(value)),
+ index, isSelected, cellHasFocus);
+ }
+
+ });
+ setSelectedItem(property.getValue());
+
+ addPopupMenuListener(new PopupMenuListener() {
+ @Override
+ public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
+ // Schedule the update just after the popup becomes visible
+ SwingUtilities.invokeLater(() -> {
+ if (getModel() instanceof DynamicComboBoxModel model) {
+ model.refreshValues();
+ }
+ });
+ }
+
+ @Override
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {}
+
+ @Override
+ public void popupMenuCanceled(PopupMenuEvent e) {}
+ });
+
+ addItemListener(new ItemListener() {
+
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() == ItemEvent.SELECTED) {
+ property.setChanged(true);
+ setBackground(oldBg);
+ DynamicValueEditor.this.property.setValue(e.getItem());
+ }
+ }
+ });
+ if (!property.isUnique()) {
+ setBackground(Constants.MULTI_VALUE_COLOR);
+ }
+ }
+
+ class DynamicComboBoxModel implements ComboBoxModel {
+ private final Vector items;
+ private Object selectedItem;
+ private final Vector listeners;
+ private final IDynamicPropertySource dynamicPropertySource;
+
+ public DynamicComboBoxModel(IDynamicPropertySource source) {
+ this.dynamicPropertySource = source;
+ this.items = new Vector<>();
+ this.listeners = new Vector<>();
+ refreshValues();
+ }
+
+ private void refreshValues() {
+ if (dynamicPropertySource != null) {
+ java.util.List> newValues = dynamicPropertySource.getAvailableValues();
+ if (!items.equals(newValues)) {
+ Object selected = selectedItem;
+ items.clear();
+ items.addAll(newValues);
+
+ // Single update event
+ ListDataEvent event = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, items.size());
+ for (ListDataListener listener : listeners) {
+ listener.contentsChanged(event);
+ }
+
+ // Restore selection if possible
+ if (selected != null && items.contains(selected)) {
+ selectedItem = selected;
+ } else if (!items.isEmpty()) {
+ selectedItem = items.get(0);
+ } else {
+ selectedItem = null;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setSelectedItem(Object anItem) {
+ if ((selectedItem != null && !selectedItem.equals(anItem)) ||
+ selectedItem == null && anItem != null) {
+ selectedItem = anItem;
+ }
+ }
+
+ @Override
+ public Object getSelectedItem() {
+ return selectedItem;
+ }
+
+ @Override
+ public int getSize() {
+ return items.size();
+ }
+
+ @Override
+ public Object getElementAt(int index) {
+ return items.get(index);
+ }
+
+ @Override
+ public void addListDataListener(ListDataListener l) {
+ listeners.add(l);
+ }
+
+ @Override
+ public void removeListDataListener(ListDataListener l) {
+ listeners.remove(l);
+ }
+ }
+}
diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/editor/FieldEditorFactory.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/editor/FieldEditorFactory.java
index 0bd7337b..a840540b 100644
--- a/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/editor/FieldEditorFactory.java
+++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/editor/FieldEditorFactory.java
@@ -95,6 +95,10 @@ public class FieldEditorFactory {
EnumEditor editor = new EnumEditor(property);
return editor;
}
+ if (property.getDynamicPropertySource() != null) {
+ DynamicValueEditor editor = new DynamicValueEditor(property);
+ return editor;
+ }
if (Byte.class.isAssignableFrom(property.getType()) || byte.class.isAssignableFrom(property.getType())) {
ByteEditor editor = new ByteEditor(property);
return editor;