mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-10 17:22:09 +10:00
switch position display
This commit is contained in:
@@ -24,6 +24,7 @@ package org.diylc.common;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.diylc.core.IDynamicPropertySource;
|
||||||
import org.diylc.core.IPropertyValidator;
|
import org.diylc.core.IPropertyValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,9 +46,10 @@ public class PropertyWrapper implements Cloneable {
|
|||||||
private boolean changed = false;
|
private boolean changed = false;
|
||||||
private int sortOrder;
|
private int sortOrder;
|
||||||
private Object ownerObject;
|
private Object ownerObject;
|
||||||
|
private IDynamicPropertySource dynamicPropertySource;
|
||||||
|
|
||||||
public PropertyWrapper(String name, Class<?> type, String getter, String setter, boolean defaultable,
|
public PropertyWrapper(String name, Class<?> type, String getter, String setter, boolean defaultable,
|
||||||
IPropertyValidator validator, int sortOrder) {
|
IPropertyValidator validator, int sortOrder, IDynamicPropertySource dynamicPropertySource) {
|
||||||
super();
|
super();
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -56,6 +58,7 @@ public class PropertyWrapper implements Cloneable {
|
|||||||
this.defaultable = defaultable;
|
this.defaultable = defaultable;
|
||||||
this.validator = validator;
|
this.validator = validator;
|
||||||
this.sortOrder = sortOrder;
|
this.sortOrder = sortOrder;
|
||||||
|
this.dynamicPropertySource = dynamicPropertySource;
|
||||||
this.ownerObject = null;
|
this.ownerObject = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +141,10 @@ public class PropertyWrapper implements Cloneable {
|
|||||||
return sortOrder;
|
return sortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IDynamicPropertySource getDynamicPropertySource() {
|
||||||
|
return dynamicPropertySource;
|
||||||
|
}
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
// public Object clone() throws CloneNotSupportedException {
|
// public Object clone() throws CloneNotSupportedException {
|
||||||
// // Try to invoke clone method on value if possible.
|
// // Try to invoke clone method on value if possible.
|
||||||
@@ -152,7 +159,7 @@ public class PropertyWrapper implements Cloneable {
|
|||||||
public Object clone() throws CloneNotSupportedException {
|
public Object clone() throws CloneNotSupportedException {
|
||||||
PropertyWrapper clone =
|
PropertyWrapper clone =
|
||||||
new PropertyWrapper(this.name, this.type, this.getter, this.setter, this.defaultable, this.validator,
|
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.value = this.value;
|
||||||
clone.changed = this.changed;
|
clone.changed = this.changed;
|
||||||
clone.unique = this.unique;
|
clone.unique = this.unique;
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package org.diylc.core;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IDynamicPropertySource {
|
||||||
|
|
||||||
|
void setComponent(IDIYComponent<?> component);
|
||||||
|
|
||||||
|
List<Object> getAvailableValues();
|
||||||
|
|
||||||
|
String getDisplayValue(Object value);
|
||||||
|
}
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.diylc.core;
|
package org.diylc.core;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for all component that can act as switches. They must have at least one position.
|
* 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 {
|
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.
|
* @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.
|
* @return true if the two points are connected in the given switch position, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean arePointsConnected(int index1, int index2, int position);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<Object> getAvailableValues() {
|
||||||
|
if (component == null) {
|
||||||
|
throw new RuntimeException("Component not set");
|
||||||
|
}
|
||||||
|
if (!(component instanceof ISwitch sw)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
int positionCount = sw.getPositionCount();
|
||||||
|
List<Object> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
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() ;
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
import org.diylc.core.AllowAllValidator;
|
import org.diylc.core.AllowAllValidator;
|
||||||
|
import org.diylc.core.IDynamicPropertySource;
|
||||||
import org.diylc.core.IPropertyValidator;
|
import org.diylc.core.IPropertyValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,11 +41,9 @@ import org.diylc.common.IComponentTransformer;
|
|||||||
import org.diylc.common.PropertyWrapper;
|
import org.diylc.common.PropertyWrapper;
|
||||||
import org.diylc.core.CreationMethod;
|
import org.diylc.core.CreationMethod;
|
||||||
import org.diylc.core.IDIYComponent;
|
import org.diylc.core.IDIYComponent;
|
||||||
|
import org.diylc.core.IDynamicPropertySource;
|
||||||
import org.diylc.core.IPropertyValidator;
|
import org.diylc.core.IPropertyValidator;
|
||||||
import org.diylc.core.annotations.BomPolicy;
|
import org.diylc.core.annotations.*;
|
||||||
import org.diylc.core.annotations.ComponentDescriptor;
|
|
||||||
import org.diylc.core.annotations.EditableProperty;
|
|
||||||
import org.diylc.core.annotations.KeywordPolicy;
|
|
||||||
import org.diylc.lang.LangUtil;
|
import org.diylc.lang.LangUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -190,9 +188,15 @@ public class ComponentProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IDynamicPropertySource dynamicPropertySource = null;
|
||||||
|
if (getter.isAnnotationPresent(DynamicEditableProperty.class)) {
|
||||||
|
DynamicEditableProperty dynamicAnnotation = getter.getAnnotation(DynamicEditableProperty.class);
|
||||||
|
dynamicPropertySource = getDynamicPropertySource(dynamicAnnotation.source());
|
||||||
|
}
|
||||||
|
|
||||||
PropertyWrapper property =
|
PropertyWrapper property =
|
||||||
new PropertyWrapper(name, getter.getReturnType(), getter.getName(), setterName,
|
new PropertyWrapper(name, getter.getReturnType(), getter.getName(), setterName,
|
||||||
annotation.defaultable(), validator, annotation.sortOrder());
|
annotation.defaultable(), validator, annotation.sortOrder(), dynamicPropertySource);
|
||||||
properties.add(property);
|
properties.add(property);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,6 +282,17 @@ public class ComponentProcessor {
|
|||||||
return validator;
|
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) {
|
private IComponentTransformer getComponentTransformer(Class<? extends IComponentTransformer> clazz) {
|
||||||
if (clazz == null)
|
if (clazz == null)
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
package org.diylc.utils;
|
package org.diylc.utils;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
package org.diylc.utils;
|
package org.diylc.utils;
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package org.diylc.utils;
|
||||||
|
|
||||||
|
import org.diylc.core.ISwitch;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class SwitchUtils {
|
||||||
|
|
||||||
|
public static List<Set<Integer>> getConnectedTerminals(ISwitch switchComponent, int numPoints) {
|
||||||
|
List<Set<Integer>> 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<int[]> 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<Integer, Set<Integer>> pointToGroup = new HashMap<>();
|
||||||
|
|
||||||
|
// Process each connection
|
||||||
|
for (int[] connection : connections) {
|
||||||
|
int p1 = connection[0];
|
||||||
|
int p2 = connection[1];
|
||||||
|
|
||||||
|
Set<Integer> group1 = pointToGroup.get(p1);
|
||||||
|
Set<Integer> group2 = pointToGroup.get(p2);
|
||||||
|
|
||||||
|
if (group1 == null && group2 == null) {
|
||||||
|
// Create new group
|
||||||
|
Set<Integer> 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<Set<Integer>> uniqueGroups = new HashSet<>(pointToGroup.values());
|
||||||
|
connectedGroups.addAll(uniqueGroups);
|
||||||
|
|
||||||
|
return connectedGroups;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<int[]> connections;
|
||||||
|
private final Integer selectedPosition;
|
||||||
|
private final boolean renderConnected;
|
||||||
|
|
||||||
|
public MockSwitch(List<int[]> 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<Set<Integer>> groups = SwitchUtils.getConnectedTerminals(mockSwitch, 5);
|
||||||
|
assertTrue("Should return empty list when no connections", groups.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleConnection() {
|
||||||
|
List<int[]> connections = Arrays.asList(new int[]{0, 1});
|
||||||
|
MockSwitch mockSwitch = new MockSwitch(connections, 0, true);
|
||||||
|
List<Set<Integer>> 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<int[]> connections = Arrays.asList(
|
||||||
|
new int[]{1, 2},
|
||||||
|
new int[]{2, 3}
|
||||||
|
);
|
||||||
|
MockSwitch mockSwitch = new MockSwitch(connections, 0, true);
|
||||||
|
List<Set<Integer>> 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<int[]> 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<Set<Integer>> 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<int[]> connections = Arrays.asList(new int[]{0, 1});
|
||||||
|
MockSwitch mockSwitch = new MockSwitch(connections, 0, false);
|
||||||
|
List<Set<Integer>> groups = SwitchUtils.getConnectedTerminals(mockSwitch, 5);
|
||||||
|
assertTrue("Should return empty list when renderConnected is false", groups.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoSelectedPosition() {
|
||||||
|
List<int[]> connections = Arrays.asList(new int[]{0, 1});
|
||||||
|
MockSwitch mockSwitch = new MockSwitch(connections, null, true);
|
||||||
|
List<Set<Integer>> 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<int[]> 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<Set<Integer>> 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))));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,28 +32,26 @@ import java.awt.geom.Ellipse2D;
|
|||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||||
|
|
||||||
import org.diylc.common.IPlugInPort;
|
import org.diylc.common.IPlugInPort;
|
||||||
import org.diylc.common.ObjectCache;
|
import org.diylc.common.ObjectCache;
|
||||||
import org.diylc.components.AbstractAngledComponent;
|
import org.diylc.components.AbstractAngledComponent;
|
||||||
import org.diylc.components.transform.AngledComponentTransformer;
|
import org.diylc.components.transform.AngledComponentTransformer;
|
||||||
import org.diylc.core.ComponentState;
|
import org.diylc.core.*;
|
||||||
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.annotations.ComponentDescriptor;
|
import org.diylc.core.annotations.ComponentDescriptor;
|
||||||
|
import org.diylc.core.annotations.DynamicEditableProperty;
|
||||||
import org.diylc.core.annotations.EditableProperty;
|
import org.diylc.core.annotations.EditableProperty;
|
||||||
import org.diylc.core.annotations.KeywordPolicy;
|
import org.diylc.core.annotations.KeywordPolicy;
|
||||||
import org.diylc.core.measures.Size;
|
import org.diylc.core.measures.Size;
|
||||||
import org.diylc.core.measures.SizeUnit;
|
import org.diylc.core.measures.SizeUnit;
|
||||||
import org.diylc.utils.Constants;
|
import org.diylc.utils.Constants;
|
||||||
|
|
||||||
|
import static org.diylc.utils.SwitchUtils.getConnectedTerminals;
|
||||||
|
|
||||||
@ComponentDescriptor(name = "Lever Switch", category = "Guitar", author = "Branislav Stojkovic",
|
@ComponentDescriptor(name = "Lever Switch", category = "Guitar", author = "Branislav Stojkovic",
|
||||||
description = "Strat-style lever switch", zOrder = IDIYComponent.COMPONENT,
|
description = "Strat-style lever switch", zOrder = IDIYComponent.COMPONENT,
|
||||||
instanceNamePrefix = "SW", keywordPolicy = KeywordPolicy.SHOW_TAG,
|
instanceNamePrefix = "SW", keywordPolicy = KeywordPolicy.SHOW_TAG,
|
||||||
@@ -65,7 +63,6 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
private static Color BASE_COLOR = Color.lightGray;
|
private static Color BASE_COLOR = Color.lightGray;
|
||||||
private static Color WAFER_COLOR = Color.decode("#CD8500");
|
private static Color WAFER_COLOR = Color.decode("#CD8500");
|
||||||
private static Color LUG_COLOR = METAL_COLOR;
|
private static Color LUG_COLOR = METAL_COLOR;
|
||||||
private static Color COMMON_LUG_COLOR = Color.decode("#FF9999");
|
|
||||||
|
|
||||||
private static Size BASE_WIDTH = new Size(10d, SizeUnit.mm);
|
private static Size BASE_WIDTH = new Size(10d, SizeUnit.mm);
|
||||||
private static Size BASE_LENGTH = new Size(47.5d, SizeUnit.mm);
|
private static Size BASE_LENGTH = new Size(47.5d, SizeUnit.mm);
|
||||||
@@ -83,7 +80,10 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
private transient String value = "";
|
private transient String value = "";
|
||||||
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
|
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
|
||||||
private LeverSwitchType type = LeverSwitchType.DP3T;
|
private LeverSwitchType type = LeverSwitchType.DP3T;
|
||||||
|
@Deprecated
|
||||||
private Boolean highlightCommon;
|
private Boolean highlightCommon;
|
||||||
|
private Integer selectedPosition;
|
||||||
|
private Boolean renderConnectedTerminals;
|
||||||
|
|
||||||
public LeverSwitch() {
|
public LeverSwitch() {
|
||||||
super();
|
super();
|
||||||
@@ -142,10 +142,12 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
g2d.fill(body[2]);
|
g2d.fill(body[2]);
|
||||||
g2d.setColor(LUG_COLOR.darker());
|
g2d.setColor(LUG_COLOR.darker());
|
||||||
g2d.draw(body[2]);
|
g2d.draw(body[2]);
|
||||||
g2d.setColor(COMMON_LUG_COLOR);
|
for (int i = 3; i < body.length; i++) {
|
||||||
g2d.fill(body[3]);
|
g2d.setColor(ISwitch.POLE_COLORS[i - 3]);
|
||||||
g2d.setColor(COMMON_LUG_COLOR.darker());
|
g2d.fill(body[i]);
|
||||||
g2d.draw(body[3]);
|
g2d.setColor(ISwitch.POLE_COLORS[i - 3].darker());
|
||||||
|
g2d.draw(body[i]);
|
||||||
|
}
|
||||||
|
|
||||||
// g2d.setColor(Color.black);
|
// g2d.setColor(Color.black);
|
||||||
// for(int i = 0; i < getControlPointCount(); i++) {
|
// for(int i = 0; i < getControlPointCount(); i++) {
|
||||||
@@ -156,7 +158,6 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
@SuppressWarnings("incomplete-switch")
|
@SuppressWarnings("incomplete-switch")
|
||||||
public Shape[] getBody() {
|
public Shape[] getBody() {
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
body = new Shape[4];
|
|
||||||
|
|
||||||
double x = controlPoints[0].getX();
|
double x = controlPoints[0].getX();
|
||||||
double y = controlPoints[0].getY();
|
double y = controlPoints[0].getY();
|
||||||
@@ -192,7 +193,7 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
+ (baseLength - holeSpacing) / 2 - holeSize / 2, holeSize, holeSize)));
|
+ (baseLength - holeSpacing) / 2 - holeSize / 2, holeSize, holeSize)));
|
||||||
baseArea.subtract(new Area(new Ellipse2D.Double(baseX + baseWidth / 2 - holeSize / 2, baseY
|
baseArea.subtract(new Area(new Ellipse2D.Double(baseX + baseWidth / 2 - holeSize / 2, baseY
|
||||||
+ (baseLength - holeSpacing) / 2 - holeSize / 2 + holeSpacing, holeSize, holeSize)));
|
+ (baseLength - holeSpacing) / 2 - holeSize / 2 + holeSpacing, holeSize, holeSize)));
|
||||||
body[0] = baseArea;
|
|
||||||
|
|
||||||
Area waferArea =
|
Area waferArea =
|
||||||
new Area(new Rectangle2D.Double(x - terminalLength / 2 - waferThickness / 2, y
|
new Area(new Rectangle2D.Double(x - terminalLength / 2 - waferThickness / 2, y
|
||||||
@@ -204,12 +205,18 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
- (waferLength - terminalSpacing * 12) / 2, waferThickness,
|
- (waferLength - terminalSpacing * 12) / 2, waferThickness,
|
||||||
waferLength)));
|
waferLength)));
|
||||||
}
|
}
|
||||||
body[1] = waferArea;
|
|
||||||
|
|
||||||
double theta = getAngle().getValueRad();
|
double theta = getAngle().getValueRad();
|
||||||
|
|
||||||
Area terminalArea = new Area();
|
List<Set<Integer>> connectedTerminals = getConnectedTerminals(this, controlPoints.length);
|
||||||
Area commonTerminalArea = new Area();
|
|
||||||
|
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++) {
|
for (int i = 0; i < controlPoints.length; i++) {
|
||||||
Point2D point = controlPoints[i];
|
Point2D point = controlPoints[i];
|
||||||
Area terminal =
|
Area terminal =
|
||||||
@@ -222,19 +229,14 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
AffineTransform rotation = AffineTransform.getRotateInstance(theta, point.getX(), point.getY());
|
AffineTransform rotation = AffineTransform.getRotateInstance(theta, point.getX(), point.getY());
|
||||||
terminal.transform(rotation);
|
terminal.transform(rotation);
|
||||||
}
|
}
|
||||||
terminalArea.add(terminal);
|
|
||||||
if (getHighlightCommon() &&
|
int finalI = i;
|
||||||
(((type == LeverSwitchType.DP3T || type == LeverSwitchType.DP3T_5pos) && (i == 1 || i == 6)) ||
|
int groupIndex = IntStream.range(0, connectedTerminals.size())
|
||||||
((type == LeverSwitchType._6_WAY_OG) && (i == 1 || i == 8)) ||
|
.filter(j -> connectedTerminals.get(j).contains(finalI))
|
||||||
(type == LeverSwitchType.DP4T && (i == 1 || i == 8)) ||
|
.findFirst().orElse(-1);
|
||||||
(type == LeverSwitchType.DP3T_5pos_Import && (i == 3 || i == 4)) ||
|
|
||||||
((type == LeverSwitchType._4P5T || type == LeverSwitchType.DP5T) && (i == 0 || i == 11 || i == 12 || i == 23))))
|
((Area)body[3 + groupIndex]).add(terminal);
|
||||||
commonTerminalArea.add(terminal);
|
|
||||||
else
|
|
||||||
terminalArea.add(terminal);
|
|
||||||
}
|
}
|
||||||
body[2] = terminalArea;
|
|
||||||
body[3] = commonTerminalArea;
|
|
||||||
|
|
||||||
// Rotate if needed
|
// Rotate if needed
|
||||||
if (theta != 0) {
|
if (theta != 0) {
|
||||||
@@ -250,6 +252,8 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("incomplete-switch")
|
@SuppressWarnings("incomplete-switch")
|
||||||
protected void updateControlPoints() {
|
protected void updateControlPoints() {
|
||||||
double x = controlPoints[0].getX();
|
double x = controlPoints[0].getX();
|
||||||
@@ -383,21 +387,6 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
this.body = null;
|
this.body = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EditableProperty(name = "Mark Common Lugs")
|
|
||||||
public Boolean getHighlightCommon() {
|
|
||||||
if (highlightCommon == null)
|
|
||||||
highlightCommon = true;
|
|
||||||
return highlightCommon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHighlightCommon(Boolean highlightCommon) {
|
|
||||||
this.highlightCommon = highlightCommon;
|
|
||||||
|
|
||||||
body = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
// public String getControlPointNodeName(int index) {
|
// public String getControlPointNodeName(int index) {
|
||||||
// // we don't want the switch to produce any nodes, it just makes connections
|
// // we don't want the switch to produce any nodes, it just makes connections
|
||||||
@@ -533,6 +522,32 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
|||||||
) // position 6
|
) // position 6
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@DynamicEditableProperty(source = SwitchPositionPropertyValueSource.class)
|
||||||
|
@EditableProperty(name = "Selected Position")
|
||||||
|
@Override
|
||||||
|
public Integer getSelectedPosition() {
|
||||||
|
return selectedPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectedPosition(Integer selectedPosition) {
|
||||||
|
this.selectedPosition = selectedPosition;
|
||||||
|
this.body = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EditableProperty(name = "Visualize Connected")
|
||||||
|
@Override
|
||||||
|
public Boolean getRenderConnectedTerminals() {
|
||||||
|
if (renderConnectedTerminals == null) {
|
||||||
|
renderConnectedTerminals = false;
|
||||||
|
}
|
||||||
|
return renderConnectedTerminals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenderConnectedTerminals(Boolean renderConnectedTerminals) {
|
||||||
|
this.renderConnectedTerminals = renderConnectedTerminals;
|
||||||
|
this.body = null;
|
||||||
|
}
|
||||||
|
|
||||||
public static enum LeverSwitchType {
|
public static enum LeverSwitchType {
|
||||||
|
|
||||||
DP3T("DP3T (Standard 3-Position Strat)"), DP3T_5pos("DP3T (Standard 5-Position Strat)"), DP3T_5pos_Import("DP3T (Import 5-Position Strat)"),
|
DP3T("DP3T (Standard 3-Position Strat)"), DP3T_5pos("DP3T (Standard 5-Position Strat)"), DP3T_5pos_Import("DP3T (Import 5-Position Strat)"),
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class CheckProximityAction extends AbstractAction implements Cloneable {
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
PropertyWrapper wrapper = new PropertyWrapper("Threshold", Size.class, "getThreshold",
|
PropertyWrapper wrapper = new PropertyWrapper("Threshold", Size.class, "getThreshold",
|
||||||
"setThreshold", false, new PositiveNonZeroMeasureValidator(), 0);
|
"setThreshold", false, new PositiveNonZeroMeasureValidator(), 0, null);
|
||||||
try {
|
try {
|
||||||
wrapper.readFrom(CheckProximityAction.this);
|
wrapper.readFrom(CheckProximityAction.this);
|
||||||
} catch (Exception e1) {
|
} catch (Exception e1) {
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
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<Object> {
|
||||||
|
|
||||||
|
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<Object>(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<Object> {
|
||||||
|
private final Vector<Object> items;
|
||||||
|
private Object selectedItem;
|
||||||
|
private final Vector<ListDataListener> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,6 +95,10 @@ public class FieldEditorFactory {
|
|||||||
EnumEditor editor = new EnumEditor(property);
|
EnumEditor editor = new EnumEditor(property);
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
if (property.getDynamicPropertySource() != null) {
|
||||||
|
DynamicValueEditor editor = new DynamicValueEditor(property);
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
if (Byte.class.isAssignableFrom(property.getType()) || byte.class.isAssignableFrom(property.getType())) {
|
if (Byte.class.isAssignableFrom(property.getType()) || byte.class.isAssignableFrom(property.getType())) {
|
||||||
ByteEditor editor = new ByteEditor(property);
|
ByteEditor editor = new ByteEditor(property);
|
||||||
return editor;
|
return editor;
|
||||||
|
|||||||
Reference in New Issue
Block a user