mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5dcc3f01d5 | |||
| 7a25a09a16 | |||
| 405309825d | |||
| 6ba947cbfa | |||
| 5a9cc6fff2 | |||
| d7360699c8 | |||
| 465ddbd032 | |||
| efe07e3ee7 | |||
| 66c5ce5ad2 |
@@ -2569,7 +2569,7 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
try {
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(
|
||||
"pending-variants.xml"));
|
||||
"import-defaults/variants.xml"));
|
||||
XStream xStream = new XStream(new DomDriver());
|
||||
xStream.addPermission(AnyTypePermission.ANY);
|
||||
ProjectFileManager.xStreamSerializer.toXML(defaultVariantMap, out);
|
||||
|
||||
@@ -4128,204 +4128,4 @@
|
||||
</org.diylc.core.Template>
|
||||
</list>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>org.diylc.components.electromechanical.TactileMicroSwitch</string>
|
||||
<list>
|
||||
<org.diylc.core.Template>
|
||||
<name>6x6 (e.g. Omron B3F-1000)</name>
|
||||
<values>
|
||||
<entry>
|
||||
<string>Spacing X</string>
|
||||
<org.diylc.core.measures.Size value="6.5" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Spacing Y</string>
|
||||
<org.diylc.core.measures.Size value="4.5" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<awt-color hex="ffffff"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Orientation</string>
|
||||
<org.diylc.common.Orientation>DEFAULT</org.diylc.common.Orientation>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Case</string>
|
||||
<org.diylc.core.measures.Size value="6.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Name</string>
|
||||
<string>SW2</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Font Size Override</string>
|
||||
<null/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<string></string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Alpha</string>
|
||||
<byte>127</byte>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Body</string>
|
||||
<awt-color hex="c0c0c0"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Border</string>
|
||||
<awt-color hex="595959"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Display</string>
|
||||
<org.diylc.common.Display>BOTH</org.diylc.common.Display>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Pin Pairing Mode</string>
|
||||
<diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>Opposite</diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>
|
||||
</entry>
|
||||
</values>
|
||||
<points>
|
||||
<point x="0.0" y="0.0"/>
|
||||
<point x="0.0" y="0.175"/>
|
||||
<point x="0.255" y="0.0"/>
|
||||
<point x="0.255" y="0.175"/>
|
||||
</points>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-01-02T10:01:17.805688</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>12x12 (e.g. Omron B3F-4000)</name>
|
||||
<values>
|
||||
<entry>
|
||||
<string>Spacing X</string>
|
||||
<org.diylc.core.measures.Size value="12.6" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Spacing Y</string>
|
||||
<org.diylc.core.measures.Size value="5.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<awt-color hex="ffffff"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Orientation</string>
|
||||
<org.diylc.common.Orientation>DEFAULT</org.diylc.common.Orientation>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Case</string>
|
||||
<org.diylc.core.measures.Size value="12.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Name</string>
|
||||
<string>SW1</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Font Size Override</string>
|
||||
<null/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<string></string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Alpha</string>
|
||||
<byte>127</byte>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Body</string>
|
||||
<awt-color hex="c0c0c0"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Border</string>
|
||||
<awt-color hex="595959"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Display</string>
|
||||
<org.diylc.common.Display>BOTH</org.diylc.common.Display>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Pin Pairing Mode</string>
|
||||
<diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>Opposite</diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>
|
||||
</entry>
|
||||
</values>
|
||||
<points>
|
||||
<point x="0.0" y="0.0"/>
|
||||
<point x="0.0" y="0.195"/>
|
||||
<point x="0.495" y="0.0"/>
|
||||
<point x="0.495" y="0.195"/>
|
||||
</points>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-01-02T10:00:31.950676</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>5x5 Low Profile SMD (e.g. Alps SKQG)</name>
|
||||
<values>
|
||||
<entry>
|
||||
<string>Spacing X</string>
|
||||
<org.diylc.core.measures.Size value="5.2" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Spacing Y</string>
|
||||
<org.diylc.core.measures.Size value="3.7" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<awt-color hex="ffffff"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Orientation</string>
|
||||
<org.diylc.common.Orientation>DEFAULT</org.diylc.common.Orientation>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Case</string>
|
||||
<org.diylc.core.measures.Size value="5.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Name</string>
|
||||
<string>SW2</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Font Size Override</string>
|
||||
<null/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<string></string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Alpha</string>
|
||||
<byte>127</byte>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Border</string>
|
||||
<awt-color hex="595959"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Body</string>
|
||||
<awt-color hex="c0c0c0"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Display</string>
|
||||
<org.diylc.common.Display>BOTH</org.diylc.common.Display>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Pin Pairing Mode</string>
|
||||
<diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>Diagonal</diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>
|
||||
</entry>
|
||||
</values>
|
||||
<points>
|
||||
<point x="0.0" y="0.0"/>
|
||||
<point x="0.0" y="0.145"/>
|
||||
<point x="0.2" y="0.0"/>
|
||||
<point x="0.2" y="0.145"/>
|
||||
</points>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-01-02T10:04:33.795741</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
</list>
|
||||
</entry>
|
||||
</map>
|
||||
</map>
|
||||
@@ -497,50 +497,6 @@
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added balloon tooltip next to cursor showing component width, height and length (where available) while resizing; can be disabled from Config menu</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Freeway 3x3-05 (Ultra) Switch' (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Configurable 'Tactile Micro Switch' dimensions and added a few most commonly used variants</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Exposed the most common pin pairing modes and added switching logic for 'Tactile Micro Switch' to allow using it for analysis</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>'Potentiometer Panel' can now render numerical lug markers for easier identification</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>'Auto-Wrap Label' retains line breaks from the input text</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>'Cliff 1/8" Jack', 'Open 1/8" Jack' and 'Open 1/4" Jack' can be rotated</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Fixed the offset of terminals on a DP4T Tele type of 'Lever Switch' to be aligned with the physical switch (attention - this will break existing diagrams and will require adjusting leads to match the new terminal positions)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Make the label of schematic symbols (e.g. 'Resistor Symbol') clickable as well</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Ability to resize 'Transformer Core' schematic symbol after it's already placed</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Clicking on 'Tactile Micro Switch' body doesn't register</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>New lines at the end of the 'Label' text get erroneously rendered as br HTML tags</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
@@ -71,18 +72,13 @@ public class StringUtils {
|
||||
* @return a non-empty list of strings
|
||||
*/
|
||||
public static List<String> wrap(String str, FontMetrics fm, int maxWidth) {
|
||||
// First split by actual newlines to honor them, then split by <br> tags
|
||||
List<String> lines = splitIntoLines(str);
|
||||
List<String> allLines = new ArrayList<String>();
|
||||
for (String line : lines) {
|
||||
String[] split = line.split("\\<br\\>");
|
||||
allLines.addAll(Arrays.asList(split));
|
||||
}
|
||||
if (allLines.size() == 0)
|
||||
return allLines;
|
||||
String[] split = str.split("\\<br\\>");
|
||||
List<String> lines = Arrays.stream(split).flatMap(s -> splitIntoLines(s).stream()).collect(Collectors.toList());
|
||||
if (lines.size() == 0)
|
||||
return lines;
|
||||
|
||||
ArrayList<String> strings = new ArrayList<String>();
|
||||
for (Iterator<String> iter = allLines.iterator(); iter.hasNext();)
|
||||
for (Iterator<String> iter = lines.iterator(); iter.hasNext();)
|
||||
wrapLineInto((String) iter.next(), strings, fm, maxWidth);
|
||||
return strings;
|
||||
}
|
||||
@@ -130,8 +126,8 @@ public class StringUtils {
|
||||
line = line.substring(pos).trim();
|
||||
len = line.length();
|
||||
}
|
||||
// Always add the line, even if empty, to preserve empty lines from newlines
|
||||
list.add(line);
|
||||
if (len > 0)
|
||||
list.add(line);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,24 +209,12 @@ public class StringUtils {
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
// Use -1 limit to preserve trailing empty strings so we can filter them out
|
||||
String[] parts = text.split("\\<br\\>", -1);
|
||||
String[] parts = text.split("\\<br\\>");
|
||||
if (parts.length > 1) {
|
||||
// Remove trailing empty strings to avoid rendering extra blank lines
|
||||
int lastNonEmpty = parts.length - 1;
|
||||
while (lastNonEmpty >= 0 && parts[lastNonEmpty].trim().isEmpty()) {
|
||||
lastNonEmpty--;
|
||||
}
|
||||
// If all parts are empty, don't render anything
|
||||
if (lastNonEmpty < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
FontMetrics fontMetrics = g2d.getFontMetrics();
|
||||
Rectangle stringBounds = fontMetrics.getStringBounds(parts[0], g2d).getBounds();
|
||||
int actualLength = lastNonEmpty + 1;
|
||||
for (int i = 0; i < actualLength; i++)
|
||||
drawCenteredText(g2d, parts[i], x, (int)(y - stringBounds.height * (actualLength - 1) / 2d + i * stringBounds.height), horizontalAlignment, verticalAlignment);
|
||||
for (int i = 0; i < parts.length; i++)
|
||||
drawCenteredText(g2d, parts[i], x, (int)(y - stringBounds.height * (parts.length - 1) / 2d + i * stringBounds.height), horizontalAlignment, verticalAlignment);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -363,8 +363,8 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
offset = -offset;
|
||||
}
|
||||
|
||||
drawingObserver.startTracking();
|
||||
if (getMoveLabel()) {
|
||||
drawingObserver.startTracking();
|
||||
g2d.setTransform(oldTransform);
|
||||
g2d.translate(getPoints()[2].getX(), getPoints()[2].getY());
|
||||
if (getLabelOriantation() != LabelOriantation.Horizontal) {
|
||||
@@ -374,18 +374,19 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
g2d.setTransform(oldTransform);
|
||||
} else {
|
||||
if (isStanding() || getLabelOriantation() == LabelOriantation.Horizontal) {
|
||||
drawingObserver.startTracking();
|
||||
g2d.setTransform(oldTransform);
|
||||
double x = (getPoints()[0].getX() + getPoints()[1].getX() - length) / 2.0;
|
||||
double y = (getPoints()[0].getY() + getPoints()[1].getY() - width) / 2.0;
|
||||
g2d.drawString(label, (int) (x + (length - textRect.getWidth()) / 2 + offset),
|
||||
(int) (y + calculateLabelYOffset(shapeRect, textRect, fontMetrics)));
|
||||
} else {
|
||||
drawingObserver.stopTracking();
|
||||
g2d.drawString(label, (int) (length - textRect.getWidth()) / 2 + offset,
|
||||
calculateLabelYOffset(shapeRect, textRect, fontMetrics));
|
||||
g2d.setTransform(oldTransform);
|
||||
}
|
||||
}
|
||||
drawingObserver.stopTracking();
|
||||
}
|
||||
// if (getLabelOriantation() == LabelOriantation.Horizontal) {
|
||||
//
|
||||
|
||||
+1
-2
@@ -42,7 +42,6 @@ import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMultiPartComponent;
|
||||
import org.diylc.components.electromechanical.OpenJack1_4.OpenJackType;
|
||||
import org.diylc.components.transform.OpenJackTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
@@ -57,7 +56,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Open 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Switchcraft-style open panel mount 1/4\" phono jack, stereo and mono",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = OpenJackTransformer.class, enableCache = true)
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", enableCache = true)
|
||||
public class OpenJack1_4 extends AbstractMultiPartComponent<OpenJackType> {
|
||||
|
||||
private static final float FONT_SCALE = 0.8f;
|
||||
|
||||
+1
-2
@@ -42,7 +42,6 @@ import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMultiPartComponent;
|
||||
import org.diylc.components.electromechanical.OpenJack1_8.OpenJackType;
|
||||
import org.diylc.components.transform.OpenJackTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
@@ -57,7 +56,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Open 1/8\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Switchcraft-style open panel mount 1/8\" phono jack, stereo and mono",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = OpenJackTransformer.class, enableCache = true)
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", enableCache = true)
|
||||
public class OpenJack1_8 extends AbstractMultiPartComponent<OpenJackType> {
|
||||
|
||||
private static final float FONT_SCALE = 0.6f;
|
||||
|
||||
+25
-133
@@ -35,10 +35,13 @@ import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.components.AbstractLabeledComponent;
|
||||
import org.diylc.components.guitar.LeverSwitchPositionPropertyValueSource;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
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.DynamicEditableProperty;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.gerber.IGerberComponentSimple;
|
||||
@@ -50,21 +53,22 @@ import org.diylc.utils.Constants;
|
||||
category = "Electro-Mechanical", instanceNamePrefix = "SW",
|
||||
description = "4-pin tactile momentary switch", zOrder = IDIYComponent.COMPONENT,
|
||||
keywordPolicy = KeywordPolicy.SHOW_VALUE, enableCache = true)
|
||||
public class TactileMicroSwitch extends AbstractLabeledComponent<String> implements IGerberComponentSimple,
|
||||
ISwitch {
|
||||
public class TactileMicroSwitch extends AbstractLabeledComponent<String> implements IGerberComponentSimple {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Color BODY_COLOR = Color.lightGray;
|
||||
private static final Color BORDER_COLOR = Color.gray.darker();
|
||||
private static final Color PIN_COLOR = METAL_COLOR;
|
||||
private static final Color PIN_BORDER_COLOR = PIN_COLOR.darker();
|
||||
private static final Color INDENT_COLOR = Color.gray.darker();
|
||||
private static final Color LABEL_COLOR = Color.white;
|
||||
private static final Size PIN_SIZE = new Size(0.04d, SizeUnit.in);
|
||||
private static final Size CASE_SIZE = new Size(6d, SizeUnit.mm);
|
||||
private static final Size PIN_SPACING = new Size(4.5d, SizeUnit.mm);
|
||||
private static final Size ROW_SPACING = new Size(6.5d, SizeUnit.mm);
|
||||
public static Color BODY_COLOR = Color.lightGray;
|
||||
public static Color BORDER_COLOR = Color.gray.darker();
|
||||
public static Color PIN_COLOR = METAL_COLOR;
|
||||
public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
|
||||
public static Color INDENT_COLOR = Color.gray.darker();
|
||||
public static Color LABEL_COLOR = Color.white;
|
||||
public static Size PIN_SIZE = new Size(0.04d, SizeUnit.in);
|
||||
public static int PIN_COUNT = 4;
|
||||
public static Size CASE_SIZE = new Size(6d, SizeUnit.mm);
|
||||
public static Size INDENT_SIZE = new Size(3d, SizeUnit.mm);
|
||||
private static Size PIN_SPACING = new Size(4.5d, SizeUnit.mm);
|
||||
private static Size ROW_SPACING = new Size(6.5d, SizeUnit.mm);
|
||||
|
||||
private String value = "";
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
@@ -76,12 +80,6 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
private Color labelColor = LABEL_COLOR;
|
||||
transient private Area[] body;
|
||||
|
||||
private Size caseSize;
|
||||
private Size pinSpacing;
|
||||
private Size rowSpacing;
|
||||
private PinPairingMode pinPairingMode;
|
||||
private int selectedPosition;
|
||||
|
||||
public TactileMicroSwitch() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
@@ -148,11 +146,10 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
|
||||
private void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
int PIN_COUNT = 4;
|
||||
controlPoints = new Point2D[PIN_COUNT];
|
||||
controlPoints[0] = firstPoint;
|
||||
double pinSpacing = getPinSpacing().convertToPixels();
|
||||
double rowSpacing = getRowSpacing().convertToPixels();
|
||||
double pinSpacing = PIN_SPACING.convertToPixels();
|
||||
double rowSpacing = ROW_SPACING.convertToPixels();
|
||||
// Update control points.
|
||||
double dx1;
|
||||
double dy1;
|
||||
@@ -201,8 +198,8 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
double y = controlPoints[0].getY();
|
||||
double width;
|
||||
double height;
|
||||
double caseSize = getCaseSize().convertToPixels();
|
||||
double actuatorSize = caseSize / 2;
|
||||
double caseSize = CASE_SIZE.convertToPixels();
|
||||
double indentSize = INDENT_SIZE.convertToPixels();
|
||||
|
||||
width = height = caseSize;
|
||||
x = (controlPoints[0].getX() + controlPoints[3].getX() - width) / 2;
|
||||
@@ -210,7 +207,7 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
|
||||
body[0] = new Area(new Rectangle2D.Double(x, y, width, height));
|
||||
|
||||
width = height = actuatorSize;
|
||||
width = height = indentSize;
|
||||
x = (controlPoints[0].getX() + controlPoints[3].getX() - width) / 2;
|
||||
y = (controlPoints[0].getY() + controlPoints[3].getY() - height) / 2;
|
||||
|
||||
@@ -247,7 +244,7 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
|
||||
drawingObserver.startTracking();
|
||||
drawingObserver.stopTracking();
|
||||
g2d.fill(mainArea);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
@@ -394,59 +391,6 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
this.labelColor = labelColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Case Size")
|
||||
public Size getCaseSize() {
|
||||
if (caseSize == null) {
|
||||
caseSize = CASE_SIZE;
|
||||
}
|
||||
return caseSize;
|
||||
}
|
||||
|
||||
public void setCaseSize(Size caseSize) {
|
||||
this.caseSize = caseSize;
|
||||
this.body = null;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Pin Spacing")
|
||||
public Size getPinSpacing() {
|
||||
if (pinSpacing == null) {
|
||||
pinSpacing = PIN_SPACING;
|
||||
}
|
||||
return pinSpacing;
|
||||
}
|
||||
|
||||
public void setPinSpacing(Size pinSpacing) {
|
||||
this.pinSpacing = pinSpacing;
|
||||
this.body = null;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Row Spacing")
|
||||
public Size getRowSpacing() {
|
||||
if (rowSpacing == null) {
|
||||
rowSpacing = ROW_SPACING;
|
||||
}
|
||||
return rowSpacing;
|
||||
}
|
||||
|
||||
public void setRowSpacing(Size rowSpacing) {
|
||||
this.rowSpacing = rowSpacing;
|
||||
this.body = null;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Pin Pairing Mode")
|
||||
public PinPairingMode getPinPairingMode() {
|
||||
if (pinPairingMode == null) {
|
||||
pinPairingMode = PinPairingMode.Opposite;
|
||||
}
|
||||
return pinPairingMode;
|
||||
}
|
||||
|
||||
public void setPinPairingMode(PinPairingMode pinPairingMode) {
|
||||
this.pinPairingMode = pinPairingMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
@@ -474,56 +418,4 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin,
|
||||
maxY - minY + 2 * margin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return position == 0 ? "Released" : "Pressed";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
// Only connect pins when switch is pressed (position = 1)
|
||||
if (position != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PinPairingMode mode = getPinPairingMode();
|
||||
|
||||
switch (mode) {
|
||||
case Opposite:
|
||||
// Pins on opposite sides (same row): 0-2 and 1-3
|
||||
return (index1 == 0 && index2 == 2) || (index1 == 2 && index2 == 0) ||
|
||||
(index1 == 1 && index2 == 3) || (index1 == 3 && index2 == 1);
|
||||
case Diagonal:
|
||||
// Diagonally opposite pins: 0-3 and 1-2
|
||||
return (index1 == 0 && index2 == 3) || (index1 == 3 && index2 == 0) ||
|
||||
(index1 == 1 && index2 == 2) || (index1 == 2 && index2 == 1);
|
||||
case Adjacent:
|
||||
// Pins next to each other (same column): 0-1 and 2-3
|
||||
return (index1 == 0 && index2 == 1) || (index1 == 1 && index2 == 0) ||
|
||||
(index1 == 2 && index2 == 3) || (index1 == 3 && index2 == 2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@DynamicEditableProperty(source = TactileMicroSwitchPositionPropertyValueSource.class)
|
||||
@EditableProperty(name = "Selected Position")
|
||||
@Override
|
||||
public Integer getSelectedPosition() {
|
||||
return selectedPosition;
|
||||
}
|
||||
|
||||
public void setSelectedPosition(Integer selectedPosition) {
|
||||
this.selectedPosition = selectedPosition;
|
||||
}
|
||||
|
||||
public enum PinPairingMode {
|
||||
Opposite, Diagonal, Adjacent;
|
||||
}
|
||||
}
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package org.diylc.components.electromechanical;
|
||||
|
||||
import org.diylc.common.PropertyWrapper;
|
||||
import org.diylc.components.guitar.LeverSwitch;
|
||||
import org.diylc.core.AbstractDynamicPropertyValueSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class TactileMicroSwitchPositionPropertyValueSource extends AbstractDynamicPropertyValueSource {
|
||||
|
||||
@Override
|
||||
public List<Object> getAvailableValues() {
|
||||
return List.of("Released", "Pressed");
|
||||
}
|
||||
}
|
||||
@@ -59,8 +59,8 @@ import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
import org.diylc.utils.SwitchUtils;
|
||||
|
||||
@ComponentDescriptor(name = "Freeway 3X3-03 Switch", category = "Guitar",
|
||||
author = "Branislav Stojkovic", description = "Freeway 3X3-03 (Standard) Toggle Switch",
|
||||
@ComponentDescriptor(name = "Freeway 3X3-03 Toggle", category = "Guitar",
|
||||
author = "Branislav Stojkovic", description = "Freeway 3X3-03 Toggle Switch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram",
|
||||
transformer = Freeway3x3_03Transformer.class, enableCache = true)
|
||||
|
||||
@@ -1,493 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2025 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.components.guitar;
|
||||
|
||||
import com.kitfox.svg.SVGDiagram;
|
||||
import com.kitfox.svg.SVGElement;
|
||||
import com.kitfox.svg.SVGUniverse;
|
||||
import com.kitfox.svg.ShapeElement;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.*;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.components.transform.Freeway3x3_03Transformer;
|
||||
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 java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.diylc.core.measures.SizeUnit.px;
|
||||
|
||||
@ComponentDescriptor(name = "Freeway 3X3-05 (Ultra) Switch", category = "Guitar",
|
||||
author = "Martin Morrell", description = "Freeway 3X3-05 (Ultra) Toggle Switch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram",
|
||||
transformer = Freeway3x3_03Transformer.class, enableCache = false)
|
||||
public class Freeway3x3_05 extends AbstractTransparentComponent<Void> implements ISwitch {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(Freeway3x3_05.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static Color BASE_COLOR = FR4_COLOR;// Color.DARK_GRAY;
|
||||
private static Color PAD_COLOR = COPPER_COLOR;
|
||||
private static Color LABEL_COLOR = Color.WHITE;
|
||||
private static Color CASE_COLOR = METAL_COLOR;
|
||||
private static Color MARKER_COLOR = Color.lightGray;
|
||||
|
||||
private static final double DIAMETER_SWITCH = 34.0;
|
||||
private static final double PAD_RADIUS = 15.3;
|
||||
private static final double PAD_SIZE = 1.5;
|
||||
private static final int NUM_CONTACTS = 28;
|
||||
private static final double CONTACT_SPACING_RAD = Math.PI * (360.0 / NUM_CONTACTS) / 180.0;
|
||||
private static final double CONTACT_SPACING_INITIAL = (CONTACT_SPACING_RAD * 14) + CONTACT_SPACING_RAD / 2;
|
||||
private static final double GROUND_DIAMETER = 20;
|
||||
private static final double CENTRE_LEN = 17;
|
||||
private static final double CENTRE_WID = 10;
|
||||
private static final double MARKER_OFFSET = 3.1;
|
||||
|
||||
private static final String[] PAD_NAMES = new String[] {
|
||||
"1B", "2B", "3B", "6B", "5B", "4B", "CB", "TB", "6B", "5B", "4B", "1B", "2B", "3B",
|
||||
"6A", "5A", "4A", "1A", "2A", "3A", "TA", "CA", "1A", "2A", "3A", "6A", "5A", "4A"
|
||||
};
|
||||
|
||||
private static final AffineTransform SCALE_TX = AffineTransform.getScaleInstance(0.615d, 0.615d);
|
||||
|
||||
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
|
||||
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
|
||||
private Integer selectedPosition;
|
||||
private Boolean showMarkers;
|
||||
|
||||
private transient SVGDiagram svgDiagram;
|
||||
private transient List<Shape> pads;
|
||||
private transient List<Shape> labels;
|
||||
private transient Shape base;
|
||||
private transient Shape caseShape;
|
||||
private transient double[] xOffsetsPx;
|
||||
private transient double[] yOffsetsPx;
|
||||
|
||||
public Freeway3x3_05() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
return PAD_NAMES[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
|
||||
Project project, IDrawingObserver drawingObserver) {
|
||||
|
||||
double x = controlPoints[0].getX();
|
||||
double y = controlPoints[0].getY();
|
||||
|
||||
if (getOrientation() != Orientation.DEFAULT) {
|
||||
double theta = orientation.toRadians();
|
||||
g2d.rotate(theta, x, y);
|
||||
}
|
||||
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
|
||||
double offsetX = getXOffsetsPx()[0];
|
||||
double offsetY = getYOffsetsPx()[0];
|
||||
//double origin = DIAMETER_SWITCH / 2;
|
||||
|
||||
int diameter = (int)(new Size(DIAMETER_SWITCH, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
double originX = x - offsetX + diameter / 2.0;
|
||||
double originY = y - offsetY + diameter / 2.0;
|
||||
|
||||
//Main circular shape
|
||||
Color finalBorderColor;
|
||||
if (outlineMode) {
|
||||
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
|
||||
Constants.DEFAULT_THEME);
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: theme.getOutlineColor();
|
||||
} else {
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: BASE_COLOR.darker();
|
||||
}
|
||||
Shape base = new Ellipse2D.Double( originX, originY, diameter, diameter);
|
||||
drawingObserver.startTracking();
|
||||
g2d.setColor(BASE_COLOR);
|
||||
g2d.fill(base);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setStroke(new BasicStroke(1f));
|
||||
g2d.setColor(finalBorderColor);
|
||||
g2d.draw(base);
|
||||
|
||||
//Ground circular shape
|
||||
int gndDiameter = (int)(new Size(GROUND_DIAMETER, SizeUnit.mm)).convertToPixels();
|
||||
int gndShift = (int)(new Size( (DIAMETER_SWITCH - GROUND_DIAMETER) / 2, SizeUnit.mm).convertToPixels());
|
||||
Shape gnd = new Ellipse2D.Double( originX + gndShift, originY + gndShift, gndDiameter, gndDiameter);
|
||||
g2d.setPaint(PAD_COLOR);
|
||||
g2d.fill(gnd);
|
||||
g2d.setColor(PAD_COLOR.darker());
|
||||
g2d.draw(gnd);
|
||||
|
||||
//Centre rectangle casing block
|
||||
int rectLen = (int)(new Size(CENTRE_LEN, SizeUnit.mm)).convertToPixels();
|
||||
int rectWid = (int)(new Size(CENTRE_WID, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
int rectShiftX = (int)(new Size( (DIAMETER_SWITCH - CENTRE_WID) / 2, SizeUnit.mm).convertToPixels());
|
||||
int rectShiftY = (int)(new Size( (DIAMETER_SWITCH - CENTRE_LEN) / 2, SizeUnit.mm).convertToPixels());
|
||||
|
||||
int rectangleArc = (int)(new Size(1.0, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
Shape casing = new RoundRectangle2D.Double(originX + rectShiftX, originY + rectShiftY, rectWid, rectLen, rectangleArc, rectangleArc);
|
||||
|
||||
g2d.setPaint(CASE_COLOR);
|
||||
g2d.fill(casing);
|
||||
g2d.setColor(CASE_COLOR.darker());
|
||||
g2d.draw(casing);
|
||||
|
||||
//Pads
|
||||
double circleShift = (int)(new Size( DIAMETER_SWITCH / 2, SizeUnit.mm).convertToPixels());
|
||||
double circleCentreX = originX + circleShift;
|
||||
double circleCentreY = originY + circleShift;
|
||||
double radChange = CONTACT_SPACING_RAD * 0.45;
|
||||
double extendedRadius = (int)new Size(PAD_RADIUS + PAD_SIZE, SizeUnit.mm).convertToPixels();
|
||||
double reducedRadius = (int)new Size(PAD_RADIUS - PAD_SIZE, SizeUnit.mm).convertToPixels();
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
double rad = ((i + 0.5) * CONTACT_SPACING_RAD);
|
||||
|
||||
double rotExX = Math.cos(rad) * (extendedRadius * 1.01) + circleCentreX;
|
||||
double rotExY = Math.sin(rad) * (extendedRadius * 1.01) + circleCentreY;
|
||||
double rotExPosX = Math.cos(rad + radChange) * extendedRadius + circleCentreX;
|
||||
double rotExPosY = Math.sin(rad + radChange) * extendedRadius + circleCentreY;
|
||||
double rotExNegX = Math.cos(rad - radChange) * extendedRadius + circleCentreX;
|
||||
double rotExNegY = Math.sin(rad - radChange) * extendedRadius + circleCentreY;
|
||||
double rotReX = Math.cos(rad) * (reducedRadius * 1.01) + circleCentreX;
|
||||
double rotReY = Math.sin(rad) * (reducedRadius * 1.01) + circleCentreY;
|
||||
double rotRePosX = Math.cos(rad + radChange) * reducedRadius + circleCentreX;
|
||||
double rotRePosY = Math.sin(rad + radChange) * reducedRadius + circleCentreY;
|
||||
double rotReNegX = Math.cos(rad - radChange) * reducedRadius + circleCentreX;
|
||||
double rotReNegY = Math.sin(rad - radChange) * reducedRadius + circleCentreY;
|
||||
|
||||
Path2D pad = new Path2D.Double();
|
||||
pad.moveTo(rotExNegX, rotExNegY);
|
||||
pad.quadTo(rotExX, rotExY, rotExPosX, rotExPosY);
|
||||
pad.lineTo(rotRePosX, rotRePosY);
|
||||
pad.quadTo(rotReX, rotReY,rotReNegX, rotReNegY);
|
||||
pad.lineTo(rotExNegX, rotExNegY);
|
||||
pad.closePath();
|
||||
|
||||
g2d.setPaint(PAD_COLOR);
|
||||
g2d.fill(pad);
|
||||
g2d.setColor(PAD_COLOR.darker());
|
||||
g2d.draw(pad);
|
||||
}
|
||||
|
||||
//Labels
|
||||
double offset = (int) new Size(MARKER_OFFSET, SizeUnit.mm).convertToPixels();
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
|
||||
double px = Math.sin(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS - MARKER_OFFSET);
|
||||
double py = Math.cos(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS - MARKER_OFFSET);
|
||||
|
||||
g2d.setPaint(LABEL_COLOR);
|
||||
StringUtils.drawCenteredText(g2d, PAD_NAMES[i],
|
||||
(new Size(px + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originX,
|
||||
(new Size(py + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
|
||||
//Add other text
|
||||
int makeLen = (int)(new Size(12.0, SizeUnit.mm)).convertToPixels();
|
||||
int modelLen = (int)(new Size(8.0, SizeUnit.mm)).convertToPixels();
|
||||
double mWidth = 1.8;
|
||||
int modelWid = (int)(new Size(mWidth, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
int makeShiftX = (int)(new Size( (DIAMETER_SWITCH / 2) - 7.25, SizeUnit.mm).convertToPixels());
|
||||
int makeShiftY = (int)(new Size( (DIAMETER_SWITCH / 2) - 6.0, SizeUnit.mm).convertToPixels());
|
||||
int modelShiftX = (int)(new Size( (DIAMETER_SWITCH / 2) + 5.25, SizeUnit.mm).convertToPixels());
|
||||
int modelShiftY = (int)(new Size( (DIAMETER_SWITCH / 2) - 4.0, SizeUnit.mm).convertToPixels());
|
||||
|
||||
int modelArc = (int)(new Size(mWidth, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
Shape make = new RoundRectangle2D.Double(originX + makeShiftX, originY + makeShiftY, modelWid, makeLen, modelArc, modelArc);
|
||||
Shape model = new RoundRectangle2D.Double(originX + modelShiftX, originY + modelShiftY, modelWid, modelLen, modelArc, modelArc);
|
||||
|
||||
g2d.setPaint(BASE_COLOR);
|
||||
g2d.fill(make);
|
||||
g2d.fill(model);
|
||||
g2d.setColor(PAD_COLOR.darker());
|
||||
g2d.draw(make);
|
||||
g2d.draw(model);
|
||||
|
||||
/*String makeTxt = "FREE-WAY·ULTRA";
|
||||
String modelTxt = "3X3-05";
|
||||
g2d.setPaint(LABEL_COLOR);
|
||||
StringUtils.drawCenteredText(g2d, makeTxt,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originX + makeShiftX,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originY + makeShiftY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, modelTxt,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originX + modelShiftX,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originY + modelShiftY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);*/
|
||||
|
||||
if (getShowMarkers()) {
|
||||
g2d.setColor(MARKER_COLOR);
|
||||
|
||||
for (int i = 0; i < getControlPointCount(); i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
Point2D labelPoint = new Point2D.Double(offset, 0);
|
||||
|
||||
double px = Math.sin(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS + MARKER_OFFSET);
|
||||
double py = Math.cos(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS + MARKER_OFFSET);
|
||||
|
||||
String marker = PAD_NAMES[i];
|
||||
if(marker.equals("CA")) {
|
||||
marker = "A";
|
||||
} else if (marker.equals("CB")) {
|
||||
marker = "B";
|
||||
} else if (marker.startsWith("T")) {
|
||||
continue;
|
||||
} else {
|
||||
marker = marker.substring(0, 1);
|
||||
}
|
||||
|
||||
StringUtils.drawCenteredText(g2d, marker,
|
||||
(new Size(px + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originX,
|
||||
(new Size(py + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
public double[] getXOffsetsPx() {
|
||||
if (xOffsetsPx == null) {
|
||||
xOffsetsPx = new double[NUM_CONTACTS];
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
double x = Math.sin(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * PAD_RADIUS;
|
||||
xOffsetsPx[i] = new Size(x + DIAMETER_SWITCH, SizeUnit.mm).convertToPixels();
|
||||
}
|
||||
}
|
||||
return xOffsetsPx;
|
||||
}
|
||||
|
||||
public double[] getYOffsetsPx() {
|
||||
if (yOffsetsPx == null) {
|
||||
yOffsetsPx = new double[NUM_CONTACTS];
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
double y = Math.cos(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * PAD_RADIUS;
|
||||
yOffsetsPx[i] = new Size(y + DIAMETER_SWITCH , SizeUnit.mm).convertToPixels();
|
||||
}
|
||||
}
|
||||
return yOffsetsPx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
Shape base = new Ellipse2D.Double( 0, 0, width, height);
|
||||
g2d.setColor(BASE_COLOR);
|
||||
g2d.fill(base);
|
||||
|
||||
double contactOffset = 1;
|
||||
Shape contacts = new Ellipse2D.Double( contactOffset, contactOffset, width - contactOffset * 2, height - contactOffset * 2);
|
||||
g2d.setColor(PAD_COLOR);
|
||||
g2d.fill(contacts);
|
||||
|
||||
double centreOffset = 4;
|
||||
Shape centre = new Ellipse2D.Double( centreOffset, centreOffset, width - centreOffset * 2, height - centreOffset * 2);
|
||||
g2d.setColor(BASE_COLOR);
|
||||
g2d.fill(centre);
|
||||
|
||||
double gndOffset = 9;
|
||||
Shape gnd = new Ellipse2D.Double( gndOffset, gndOffset, width - gndOffset * 2, height - gndOffset * 2);
|
||||
g2d.setColor(PAD_COLOR);
|
||||
g2d.fill(gnd);
|
||||
|
||||
double arc = 2.0;
|
||||
Shape casing = new RoundRectangle2D.Double(12, 10, 8, 12, arc, arc);
|
||||
g2d.setColor(CASE_COLOR);
|
||||
g2d.fill(casing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return NUM_CONTACTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) {
|
||||
return controlPoints[index];
|
||||
}
|
||||
|
||||
private void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
controlPoints = new Point2D[NUM_CONTACTS];
|
||||
controlPoints[0] = firstPoint;
|
||||
double[] xOffsetsPx = getXOffsetsPx();
|
||||
double[] yOffsetsPx = getYOffsetsPx();
|
||||
double theta = orientation.toRadians();
|
||||
AffineTransform tx =
|
||||
AffineTransform.getRotateInstance(theta, firstPoint.getX(), firstPoint.getY());
|
||||
for (int i = 1; i < controlPoints.length; i++) {
|
||||
controlPoints[i] = new Point2D.Double(firstPoint.getX() + (xOffsetsPx[i] - xOffsetsPx[0]),
|
||||
firstPoint.getY() + (yOffsetsPx[i] - yOffsetsPx[0]));
|
||||
tx.transform(controlPoints[i], controlPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControlPointSticky(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
|
||||
return VisibilityPolicy.ALWAYS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControlPoint(Point2D point, int index) {
|
||||
controlPoints[index].setLocation(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Void value) {}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public Orientation getOrientation() {
|
||||
if (orientation == null)
|
||||
orientation = Orientation.DEFAULT;
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public void setOrientation(Orientation orientation) {
|
||||
this.orientation = orientation;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return Integer.toString(position + 1);
|
||||
}
|
||||
|
||||
@DynamicEditableProperty(source = Freeway3x4_03SwitchPositionPropertyValueSource.class)
|
||||
@EditableProperty(name = "Selected Position")
|
||||
@Override
|
||||
public Integer getSelectedPosition() {
|
||||
return selectedPosition;
|
||||
}
|
||||
|
||||
public void setSelectedPosition(Integer selectedPosition) {
|
||||
this.selectedPosition = selectedPosition;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Markers")
|
||||
public Boolean getShowMarkers() {
|
||||
if (showMarkers == null) {
|
||||
showMarkers = false;
|
||||
}
|
||||
return showMarkers;
|
||||
}
|
||||
|
||||
public void setShowMarkers(Boolean showMarkers) {
|
||||
this.showMarkers = showMarkers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
if (position == 0 &&
|
||||
((index1 == 0 && index2 == 6) || (index1 == 6 && index2 == 11) || (index1 == 0 && index2 == 11) ||
|
||||
(index1 == 17 && index2 == 21) || (index1 == 21 && index2 == 22) || (index1 == 17 && index2 == 22))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 1 &&
|
||||
((index1 == 1 && index2 == 6) || (index1 == 6 && index2 == 12) || (index1 == 1 && index2 == 12) ||
|
||||
(index1 == 18 && index2 == 21) || (index1 == 21 && index2 == 23) || (index1 == 18 && index2 == 23))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 2 &&
|
||||
((index1 == 2 && index2 == 6) || (index1 == 6 && index2 == 13) || (index1 == 2 && index2 == 13) ||
|
||||
(index1 == 19 && index2 == 21) || (index1 == 21 && index2 == 24) || (index1 == 19 && index2 == 24))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 3 &&
|
||||
((index1 == 5 && index2 == 6) || (index1 == 6 && index2 == 10) || (index1 == 5 && index2 == 10) ||
|
||||
(index1 == 16 && index2 == 21) || (index1 == 21 && index2 == 27) || (index1 == 16 && index2 == 27))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 4 &&
|
||||
((index1 == 4 && index2 == 6) || (index1 == 6 && index2 == 9) || (index1 == 4 && index2 == 9) ||
|
||||
(index1 == 15 && index2 == 21) || (index1 == 21 && index2 == 26) || (index1 == 15 && index2 == 26))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 5 &&
|
||||
((index1 == 3 && index2 == 6) || (index1 == 6 && index2 == 8) || (index1 == 3 && index2 == 8) ||
|
||||
(index1 == 14 && index2 == 21) || (index1 == 21 && index2 == 25) || (index1 == 14 && index2 == 25))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -321,8 +321,7 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
case DP4T:
|
||||
controlPoints = new Point2D[10];
|
||||
for (int i = 0; i < controlPoints.length; i++) {
|
||||
double yPos = i % 2 == 1 ? y + (i - 2) * terminalSpacing : y + i * terminalSpacing;
|
||||
controlPoints[i] = new Point2D.Double(x + (i % 2 == 1 ? terminalLength : 0), yPos);
|
||||
controlPoints[i] = new Point2D.Double(x + (i % 2 == 1 ? terminalLength : 0), y + i * terminalSpacing);
|
||||
}
|
||||
break;
|
||||
case DP5T:
|
||||
|
||||
@@ -45,7 +45,7 @@ import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Auto-Wrap Label", author = "Branislav Stojkovic", category = "Misc",
|
||||
description = "User defined label with auto-wrapped text", instanceNamePrefix = "LAB", zOrder = IDIYComponent.TEXT, flexibleZOrder = true,
|
||||
description = "User defined label with auto-wrapped text", instanceNamePrefix = "L", zOrder = IDIYComponent.TEXT, flexibleZOrder = true,
|
||||
bomPolicy = BomPolicy.NEVER_SHOW, transformer = TextTransformer.class)
|
||||
public class WrapLabel extends AbstractComponent<String> {
|
||||
|
||||
|
||||
-75
@@ -35,12 +35,9 @@ import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.transform.PotentiometerTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.CreationMethod;
|
||||
@@ -69,12 +66,10 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
protected static Size PIN_SIZE = new Size(0.05d, SizeUnit.in);
|
||||
protected static Size NUT_SIZE = new Size(0.4d, SizeUnit.in);
|
||||
protected static Size SHAFT_SIZE = new Size(1 / 4d, SizeUnit.in);
|
||||
protected static Size MARKER_OFFSET = new Size(0.05d, SizeUnit.in);
|
||||
protected static Color BODY_COLOR = Color.lightGray;
|
||||
protected static Color WAFER_COLOR = Color.decode("#CD8500");
|
||||
protected static Color BORDER_COLOR = Color.gray;
|
||||
protected static Color NUT_COLOR = Color.decode("#CBD5DB");
|
||||
protected static Color MARKER_COLOR_DEFAULT = Color.gray;
|
||||
|
||||
|
||||
protected Size bodyDiameter = BODY_DIAMETER;
|
||||
@@ -88,8 +83,6 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
@Deprecated
|
||||
protected boolean showShaft = false;
|
||||
protected View view;
|
||||
private Boolean showMarkers;
|
||||
private Color markerColor;
|
||||
// Array of 7 elements: 3 lug connectors, 1 pot body and 3 lugs
|
||||
transient protected Area[] body = null;
|
||||
|
||||
@@ -360,50 +353,6 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
y = panelHeight / 2 + fontMetrics.getAscent();
|
||||
|
||||
g2d.drawString(getValueForDisplay(), (int) (bodyRect.getX() + x), (int) (bodyRect.getY() + y));
|
||||
|
||||
// Draw markers if enabled.
|
||||
if (getShowMarkers()) {
|
||||
g2d.setColor(outlineMode ? theme.getOutlineColor() : getMarkerColor());
|
||||
|
||||
// Get circle center and radius
|
||||
Rectangle2D circleBounds = body[3].getBounds2D();
|
||||
double centerX = circleBounds.getCenterX();
|
||||
double centerY = circleBounds.getCenterY();
|
||||
double radius = getBodyDiameter().convertToPixels() / 2;
|
||||
|
||||
// Fixed distance from circle edge (inside the circle)
|
||||
double markerOffset = MARKER_OFFSET.convertToPixels() + 2;
|
||||
double markerDistance = radius - markerOffset;
|
||||
|
||||
// Draw markers "1", "2", "3" along radial line from center to each lug, close to circle edge
|
||||
// Reverse numbering when view is ShaftDown
|
||||
boolean reverseOrder = getView() == View.ShaftDown;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Point2D lugPoint = controlPoints[i];
|
||||
|
||||
// Calculate direction from center to lug
|
||||
double dx = lugPoint.getX() - centerX;
|
||||
double dy = lugPoint.getY() - centerY;
|
||||
double distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance > 0) {
|
||||
// Normalize direction vector
|
||||
double unitX = dx / distance;
|
||||
double unitY = dy / distance;
|
||||
|
||||
// Position marker at fixed distance from circle edge along radial line
|
||||
double markerX = centerX + unitX * markerDistance;
|
||||
double markerY = centerY + unitY * markerDistance;
|
||||
|
||||
// Determine marker number based on view
|
||||
int markerNumber = reverseOrder ? (3 - i) : (i + 1);
|
||||
|
||||
StringUtils.drawCenteredText(g2d, Integer.toString(markerNumber), markerX, markerY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -525,30 +474,6 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
public void setWaferColor(Color waferColor) {
|
||||
this.waferColor = waferColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Markers")
|
||||
public Boolean getShowMarkers() {
|
||||
if (showMarkers == null) {
|
||||
showMarkers = false;
|
||||
}
|
||||
return showMarkers;
|
||||
}
|
||||
|
||||
public void setShowMarkers(Boolean showMarkers) {
|
||||
this.showMarkers = showMarkers;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Marker Color")
|
||||
public Color getMarkerColor() {
|
||||
if (markerColor == null) {
|
||||
markerColor = MARKER_COLOR_DEFAULT;
|
||||
}
|
||||
return markerColor;
|
||||
}
|
||||
|
||||
public void setMarkerColor(Color markerColor) {
|
||||
this.markerColor = markerColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInternalLinkName(int index1, int index2) {
|
||||
|
||||
@@ -157,6 +157,6 @@ public class TransformerCore extends AbstractComponent<Void> {
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-47
@@ -27,19 +27,18 @@ import java.awt.geom.Point2D;
|
||||
import org.diylc.common.IComponentTransformer;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.components.electromechanical.CliffJack1_4;
|
||||
import org.diylc.components.electromechanical.CliffJack1_8;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class CliffJackTransformer implements IComponentTransformer {
|
||||
|
||||
@Override
|
||||
public boolean canRotate(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(CliffJack1_4.class) || component.getClass().equals(CliffJack1_8.class);
|
||||
return component.getClass().equals(CliffJack1_4.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canMirror(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(CliffJack1_4.class) || component.getClass().equals(CliffJack1_8.class);
|
||||
return component.getClass().equals(CliffJack1_4.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,15 +55,8 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
component.setControlPoint(p, index);
|
||||
}
|
||||
|
||||
Orientation o;
|
||||
if (component instanceof CliffJack1_4) {
|
||||
o = ((CliffJack1_4) component).getOrientation();
|
||||
} else if (component instanceof CliffJack1_8) {
|
||||
o = ((CliffJack1_8) component).getOrientation();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
CliffJack1_4 jack = (CliffJack1_4) component;
|
||||
Orientation o = jack.getOrientation();
|
||||
int oValue = o.ordinal();
|
||||
oValue += direction;
|
||||
if (oValue < 0)
|
||||
@@ -72,33 +64,18 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
if (oValue >= Orientation.values().length)
|
||||
oValue = 0;
|
||||
o = Orientation.values()[oValue];
|
||||
|
||||
if (component instanceof CliffJack1_4) {
|
||||
((CliffJack1_4) component).setOrientation(o);
|
||||
} else if (component instanceof CliffJack1_8) {
|
||||
((CliffJack1_8) component).setOrientation(o);
|
||||
}
|
||||
jack.setOrientation(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mirror(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
if (!(component instanceof CliffJack1_4) && !(component instanceof CliffJack1_8)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDIYComponent<?> ic = component;
|
||||
CliffJack1_4 ic = (CliffJack1_4) component;
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
double dx = 2 * (center.getX() - ic.getControlPoint(1).getX());
|
||||
double dy = 0;
|
||||
|
||||
Orientation o;
|
||||
if (component instanceof CliffJack1_4) {
|
||||
o = ((CliffJack1_4) component).getOrientation();
|
||||
} else {
|
||||
o = ((CliffJack1_8) component).getOrientation();
|
||||
}
|
||||
|
||||
Orientation o = ic.getOrientation();
|
||||
switch (o) {
|
||||
case DEFAULT:
|
||||
dy += (ic.getControlPoint(0).getY() - ic.getControlPoint(ic.getControlPointCount() - 1).getY());
|
||||
@@ -121,22 +98,12 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
ic.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY() + dy), i);
|
||||
}
|
||||
|
||||
if (component instanceof CliffJack1_4) {
|
||||
((CliffJack1_4) component).setOrientation(o);
|
||||
} else {
|
||||
((CliffJack1_8) component).setOrientation(o);
|
||||
}
|
||||
ic.setOrientation(o);
|
||||
} else {
|
||||
double dx = 0;
|
||||
double dy = 2 * (center.getY() - ic.getControlPoint(1).getY());
|
||||
|
||||
Orientation o;
|
||||
if (component instanceof CliffJack1_4) {
|
||||
o = ((CliffJack1_4) component).getOrientation();
|
||||
} else {
|
||||
o = ((CliffJack1_8) component).getOrientation();
|
||||
}
|
||||
|
||||
Orientation o = ic.getOrientation();
|
||||
switch (o) {
|
||||
case DEFAULT:
|
||||
// dx += ic.getControlPoint(0).getX() - ic.getControlPoint(ic.getControlPointCount() - 1).getX();
|
||||
@@ -161,11 +128,7 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
ic.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY() + dy), i);
|
||||
}
|
||||
|
||||
if (component instanceof CliffJack1_4) {
|
||||
((CliffJack1_4) component).setOrientation(o);
|
||||
} else {
|
||||
((CliffJack1_8) component).setOrientation(o);
|
||||
}
|
||||
ic.setOrientation(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 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.components.transform;
|
||||
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import org.diylc.common.IComponentTransformer;
|
||||
import org.diylc.components.electromechanical.OpenJack1_4;
|
||||
import org.diylc.components.electromechanical.OpenJack1_8;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class OpenJackTransformer implements IComponentTransformer {
|
||||
|
||||
@Override
|
||||
public boolean canRotate(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(OpenJack1_4.class) || component.getClass().equals(OpenJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canMirror(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(OpenJack1_4.class) || component.getClass().equals(OpenJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mirroringChangesCircuit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rotate(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
AffineTransform rotate = AffineTransform.getRotateInstance(Math.PI / 2 * direction, center.getX(), center.getY());
|
||||
for (int index = 0; index < component.getControlPointCount(); index++) {
|
||||
Point2D p = new Point2D.Double();
|
||||
rotate.transform(component.getControlPoint(index), p);
|
||||
component.setControlPoint(p, index);
|
||||
}
|
||||
|
||||
Integer angle;
|
||||
if (component instanceof OpenJack1_4) {
|
||||
angle = ((OpenJack1_4) component).getAngle();
|
||||
} else if (component instanceof OpenJack1_8) {
|
||||
angle = ((OpenJack1_8) component).getAngle();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (angle == null) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
int newAngle = angle + direction * 90;
|
||||
// Normalize to 0-360 range
|
||||
while (newAngle < 0) {
|
||||
newAngle += 360;
|
||||
}
|
||||
while (newAngle >= 360) {
|
||||
newAngle -= 360;
|
||||
}
|
||||
|
||||
if (component instanceof OpenJack1_4) {
|
||||
((OpenJack1_4) component).setAngle(newAngle);
|
||||
} else if (component instanceof OpenJack1_8) {
|
||||
((OpenJack1_8) component).setAngle(newAngle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mirror(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
if (!(component instanceof OpenJack1_4) && !(component instanceof OpenJack1_8)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDIYComponent<?> ic = component;
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
// Mirror control points horizontally
|
||||
for (int i = 0; i < ic.getControlPointCount(); i++) {
|
||||
Point2D p = ic.getControlPoint(i);
|
||||
double dx = 2 * (center.getX() - p.getX());
|
||||
ic.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY()), i);
|
||||
}
|
||||
|
||||
Integer angle;
|
||||
if (component instanceof OpenJack1_4) {
|
||||
angle = ((OpenJack1_4) component).getAngle();
|
||||
} else {
|
||||
angle = ((OpenJack1_8) component).getAngle();
|
||||
}
|
||||
|
||||
if (angle == null) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
// Mirror horizontally: reflect angle across vertical axis
|
||||
int newAngle = 360 - angle;
|
||||
while (newAngle >= 360) {
|
||||
newAngle -= 360;
|
||||
}
|
||||
|
||||
if (component instanceof OpenJack1_4) {
|
||||
((OpenJack1_4) component).setAngle(newAngle);
|
||||
} else {
|
||||
((OpenJack1_8) component).setAngle(newAngle);
|
||||
}
|
||||
} else {
|
||||
// Mirror control points vertically
|
||||
for (int i = 0; i < ic.getControlPointCount(); i++) {
|
||||
Point2D p = ic.getControlPoint(i);
|
||||
double dy = 2 * (center.getY() - p.getY());
|
||||
ic.setControlPoint(new Point2D.Double(p.getX(), p.getY() + dy), i);
|
||||
}
|
||||
|
||||
Integer angle;
|
||||
if (component instanceof OpenJack1_4) {
|
||||
angle = ((OpenJack1_4) component).getAngle();
|
||||
} else {
|
||||
angle = ((OpenJack1_8) component).getAngle();
|
||||
}
|
||||
|
||||
if (angle == null) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
// Mirror vertically: reflect angle across horizontal axis
|
||||
int newAngle = 180 - angle;
|
||||
while (newAngle < 0) {
|
||||
newAngle += 360;
|
||||
}
|
||||
while (newAngle >= 360) {
|
||||
newAngle -= 360;
|
||||
}
|
||||
|
||||
if (component instanceof OpenJack1_4) {
|
||||
((OpenJack1_4) component).setAngle(newAngle);
|
||||
} else {
|
||||
((OpenJack1_8) component).setAngle(newAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 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.awt;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class StringUtilsTest {
|
||||
|
||||
private FontMetrics fontMetrics;
|
||||
private Graphics2D g2d;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
|
||||
g2d = image.createGraphics();
|
||||
g2d.setFont(new Font("Arial", Font.PLAIN, 12));
|
||||
fontMetrics = g2d.getFontMetrics();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewline() {
|
||||
String text = "Line 1\nLine 2\nLine 3";
|
||||
int maxWidth = 1000; // Large enough to not wrap
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithCarriageReturn() {
|
||||
String text = "Line 1\rLine 2\rLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithCarriageReturnLineFeed() {
|
||||
String text = "Line 1\r\nLine 2\r\nLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMixedNewlines() {
|
||||
String text = "Line 1\nLine 2\rLine 3\r\nLine 4";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 4 lines", 4, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
assertEquals("Line 4", lines.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAndBrTag() {
|
||||
String text = "Line 1\nLine 2<br>Line 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAndWrapping() {
|
||||
String text = "First line\nSecond line with very long text that should wrap";
|
||||
int maxWidth = fontMetrics.stringWidth("Second line with very long text that should"); // Force wrapping
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertTrue("Should have at least 2 lines (newline + wrapped)", lines.size() >= 2);
|
||||
assertEquals("First line", lines.get(0));
|
||||
// The second line should be wrapped, so we should have more lines
|
||||
assertTrue("Second line should be wrapped", lines.size() >= 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithEmptyLines() {
|
||||
String text = "Line 1\n\nLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines including empty line", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAtEnd() {
|
||||
String text = "Line 1\nLine 2\n";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 2 lines (no trailing empty line)", 2, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNoNewlines() {
|
||||
String text = "This is a single line of text";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should have 1 line", 1, lines.size());
|
||||
assertEquals("This is a single line of text", lines.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithBrTagOnly() {
|
||||
String text = "Line 1<br>Line 2<br>Line 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAndBrTagMixed() {
|
||||
String text = "Line 1\nLine 2<br>Line 3\nLine 4<br>Line 5";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 5 lines", 5, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
assertEquals("Line 4", lines.get(3));
|
||||
assertEquals("Line 5", lines.get(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlines() {
|
||||
String text = "Line 1\n\n\nLine 4";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 4 lines with 2 empty lines", 4, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("", lines.get(2));
|
||||
assertEquals("Line 4", lines.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlinesAtStart() {
|
||||
String text = "\n\nLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines with 2 empty lines at start", 3, lines.size());
|
||||
assertEquals("", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlinesAtEnd() {
|
||||
String text = "Line 1\n\n\n";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
// Trailing newlines: the last newline at end doesn't create an empty line
|
||||
// So \n\n\n at end creates 2 empty lines (from first 2 newlines)
|
||||
assertEquals("Should split into 3 lines (2 empty from trailing newlines)", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlinesMixed() {
|
||||
String text = "Line 1\n\nLine 3\n\n\nLine 6";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 6 lines", 6, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
assertEquals("", lines.get(3));
|
||||
assertEquals("", lines.get(4));
|
||||
assertEquals("Line 6", lines.get(5));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.diylc.netlist;
|
||||
|
||||
import org.diylc.components.guitar.Freeway3x3_05;
|
||||
import org.junit.Test;
|
||||
|
||||
public class Freeway3x3_05Tests extends AbstractSwitchTests {
|
||||
|
||||
@Test
|
||||
public void testFreeway3x3_05() {
|
||||
Freeway3x3_05 sw = new Freeway3x3_05();
|
||||
|
||||
String[] validCombinations = new String[]{
|
||||
"0,0,6", "0,6,11", "0,0,11", "0,17,21", "0,21,22", "0,17,22",
|
||||
"1,1,6", "1,6,12", "1,1,12", "1,18,21", "1,21,23", "1,18,23",
|
||||
"2,2,6", "2,6,13", "2,2,13", "2,19,21", "2,21,24", "2,19,24",
|
||||
"3,5,6", "3,6,10", "3,5,10", "3,16,21", "3,21,27", "3,16,27",
|
||||
"4,4,6", "4,6,9", "4,4,9", "4,15,21", "4,21,26", "4,15,26",
|
||||
"5,3,6", "5,6,8", "5,3,8", "5,14,21", "5,21,25", "5,14,25",
|
||||
};
|
||||
|
||||
testSwitch(sw, validCombinations);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package org.diylc.netlist;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.diylc.components.electromechanical.TactileMicroSwitch;
|
||||
import org.diylc.components.electromechanical.TactileMicroSwitch.PinPairingMode;
|
||||
|
||||
public class TactileMicroSwitchTests extends AbstractSwitchTests {
|
||||
|
||||
@Test
|
||||
public void testTactileMicroSwitchOpposite() {
|
||||
TactileMicroSwitch tactileSwitch = new TactileMicroSwitch();
|
||||
tactileSwitch.setPinPairingMode(PinPairingMode.Opposite);
|
||||
// Position 0 (Released): no connections
|
||||
// Position 1 (Pressed): pins on opposite sides (same row): 0-2 and 1-3
|
||||
String[] validCombinations = new String[] {
|
||||
"1,0,2", "1,2,0", // Position 1: 0-2 (both directions)
|
||||
"1,1,3", "1,3,1" // Position 1: 1-3 (both directions)
|
||||
};
|
||||
testSwitch(tactileSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTactileMicroSwitchDiagonal() {
|
||||
TactileMicroSwitch tactileSwitch = new TactileMicroSwitch();
|
||||
tactileSwitch.setPinPairingMode(PinPairingMode.Diagonal);
|
||||
// Position 0 (Released): no connections
|
||||
// Position 1 (Pressed): diagonally opposite pins: 0-3 and 1-2
|
||||
String[] validCombinations = new String[] {
|
||||
"1,0,3", "1,3,0", // Position 1: 0-3 (both directions)
|
||||
"1,1,2", "1,2,1" // Position 1: 1-2 (both directions)
|
||||
};
|
||||
testSwitch(tactileSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTactileMicroSwitchAdjacent() {
|
||||
TactileMicroSwitch tactileSwitch = new TactileMicroSwitch();
|
||||
tactileSwitch.setPinPairingMode(PinPairingMode.Adjacent);
|
||||
// Position 0 (Released): no connections
|
||||
// Position 1 (Pressed): pins next to each other (same column): 0-1 and 2-3
|
||||
String[] validCombinations = new String[] {
|
||||
"1,0,1", "1,1,0", // Position 1: 0-1 (both directions)
|
||||
"1,2,3", "1,3,2" // Position 1: 2-3 (both directions)
|
||||
};
|
||||
testSwitch(tactileSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -5,7 +5,11 @@ import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.swingframework.CheckBoxListDialog;
|
||||
@@ -65,7 +69,7 @@ public class ExportBlocksAction extends AbstractAction {
|
||||
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (!Objects.equals(dialog.getSelectedButtonCaption(), "OK"))
|
||||
if (dialog.getSelectedButtonCaption() != "OK")
|
||||
return;
|
||||
|
||||
Object[] selected = dialog.getSelectedOptions();
|
||||
@@ -129,4 +133,4 @@ public class ExportBlocksAction extends AbstractAction {
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-5
@@ -5,7 +5,13 @@ import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.swingframework.CheckBoxListDialog;
|
||||
@@ -67,7 +73,7 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
ActionFactory.LOG.warn("Could not find type for: " + className);
|
||||
}
|
||||
|
||||
types.sort(new Comparator<ComponentType>() {
|
||||
Collections.sort(types, new Comparator<ComponentType>() {
|
||||
|
||||
@Override
|
||||
public int compare(ComponentType o1, ComponentType o2) {
|
||||
@@ -80,7 +86,7 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (!Objects.equals(dialog.getSelectedButtonCaption(), "OK"))
|
||||
if (dialog.getSelectedButtonCaption() != "OK")
|
||||
return;
|
||||
|
||||
Object[] selected = dialog.getSelectedOptions();
|
||||
@@ -125,7 +131,7 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
|
||||
ProjectFileManager.xStreamSerializer.toXML(variantPkg, out);
|
||||
out.close();
|
||||
ActionFactory.LOG.info("Exported variants successfully");
|
||||
ActionFactory.LOG.info("Exported variants succesfully");
|
||||
} catch (IOException e) {
|
||||
ActionFactory.LOG.error("Could not export variants", e);
|
||||
}
|
||||
@@ -147,4 +153,4 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
package org.diylc.swing.plugins.explorer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.diylc.common.ComponentType;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class ComponentTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// private static Color[] layerColors = new Color[] { Color.gray, Color.decode("#ff6363"),
|
||||
// Color.decode("#293462"), Color.decode("#138588"),
|
||||
// Color.decode("#c0ca03"), Color.decode("#990000") };
|
||||
|
||||
private Map<String, ComponentType> componentTypes;
|
||||
|
||||
public ComponentTreeCellRenderer(Map<String, List<ComponentType>> componentTypeMap) {
|
||||
this.componentTypes = componentTypeMap.values().stream().flatMap(x -> x.stream())
|
||||
.collect(Collectors.toMap(x -> x.getInstanceClass().getCanonicalName(), x -> x));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
|
||||
boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||
|
||||
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
|
||||
|
||||
if (!(value instanceof DefaultMutableTreeNode)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
|
||||
Object userObject = node.getUserObject();
|
||||
|
||||
// Handle group nodes
|
||||
if (userObject instanceof ComponentTreeModel.GroupNode) {
|
||||
ComponentTreeModel.GroupNode groupNode = (ComponentTreeModel.GroupNode) userObject;
|
||||
setText(groupNode.toString());
|
||||
setIcon(null); // No icon for group nodes
|
||||
setPreferredSize(new Dimension(250, 16));
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!(userObject instanceof IDIYComponent)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
IDIYComponent<?> component = (IDIYComponent<?>) userObject;
|
||||
ComponentType componentType = componentTypes.get(component.getClass().getCanonicalName());
|
||||
|
||||
if (componentType == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Set the component's icon
|
||||
Icon icon = componentType.getIcon();
|
||||
if (icon != null) {
|
||||
setIcon(icon);
|
||||
}
|
||||
|
||||
int layerId = (int) Math.round(componentType.getZOrder());
|
||||
|
||||
String valueForDisplay = component.getValueForDisplay();
|
||||
if (selected) {
|
||||
if (valueForDisplay == null || valueForDisplay.trim().isEmpty()) {
|
||||
setText(String.format("[L%s] %s", layerId, component.getName()));
|
||||
} else {
|
||||
setText(String.format("[L%s] %s (%s)", layerId, component.getName(), valueForDisplay));
|
||||
}
|
||||
} else {
|
||||
if (valueForDisplay == null || valueForDisplay.trim().isEmpty()) {
|
||||
setText(String.format("<html><font color='#c0c0c0'>[L%s]</font> %s</html>",
|
||||
layerId, component.getName()));
|
||||
} else {
|
||||
setText(String.format(
|
||||
"<html><font color='#c0c0c0'>[L%s]</font> %s (<font color='#666666'>%s</font>)</html>",
|
||||
layerId, component.getName(), valueForDisplay));
|
||||
}
|
||||
}
|
||||
|
||||
// Set preferred size after all properties are set (like TreePanel does)
|
||||
setPreferredSize(new Dimension(250, 16));
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
-266
@@ -1,266 +0,0 @@
|
||||
package org.diylc.swing.plugins.explorer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class ComponentTreeModel extends DefaultTreeModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private DefaultMutableTreeNode root;
|
||||
private List<IDIYComponent<?>> componentsRaw;
|
||||
private List<IDIYComponent<?>> components;
|
||||
private Set<Set<IDIYComponent<?>>> groups;
|
||||
|
||||
private String searchText;
|
||||
private ExplorerPane.Sort sort = ExplorerPane.Sort.Z_INDEX;
|
||||
|
||||
public ComponentTreeModel() {
|
||||
super(new DefaultMutableTreeNode("Root"));
|
||||
this.root = (DefaultMutableTreeNode) getRoot();
|
||||
}
|
||||
|
||||
public void setComponents(List<IDIYComponent<?>> components, Set<Set<IDIYComponent<?>>> groups) {
|
||||
this.componentsRaw = components;
|
||||
this.groups = groups != null ? groups : new HashSet<>();
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void setComponents(List<IDIYComponent<?>> components) {
|
||||
setComponents(components, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group that contains the given component, or null if not in a group.
|
||||
*/
|
||||
public Set<IDIYComponent<?>> getGroupForComponent(IDIYComponent<?> component) {
|
||||
if (groups == null) {
|
||||
return null;
|
||||
}
|
||||
for (Set<IDIYComponent<?>> group : groups) {
|
||||
if (group.contains(component)) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<IDIYComponent<?>> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
public ExplorerPane.Sort getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(ExplorerPane.Sort sort) {
|
||||
this.sort = sort;
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void setSearchText(String searchText) {
|
||||
this.searchText = searchText.toLowerCase();
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
// Clear existing children
|
||||
root.removeAllChildren();
|
||||
|
||||
// Sort components
|
||||
if (sort == ExplorerPane.Sort.NAME) {
|
||||
this.components =
|
||||
componentsRaw.stream().sorted(Comparator.comparing(x -> x.toString().toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
} else if (sort == ExplorerPane.Sort.TYPE) {
|
||||
this.components = componentsRaw.stream()
|
||||
.sorted(Comparator.comparing(x -> x.getClass().getName().toLowerCase())
|
||||
.thenComparing(x -> x.toString().toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
List<IDIYComponent<?>> componentsClone = new ArrayList<IDIYComponent<?>>(componentsRaw);
|
||||
Collections.reverse(componentsClone);
|
||||
this.components = componentsClone;
|
||||
}
|
||||
|
||||
// Filter by search text
|
||||
if (searchText != null && !searchText.trim().isEmpty()) {
|
||||
this.components = this.components.stream().filter(component -> {
|
||||
return component.getName().toLowerCase().contains(searchText);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// Build a map of components to their groups
|
||||
Set<IDIYComponent<?>> groupedComponents = new HashSet<>();
|
||||
if (groups != null) {
|
||||
for (Set<IDIYComponent<?>> group : groups) {
|
||||
groupedComponents.addAll(group);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a list to hold all tree items (groups and ungrouped components) for sorting
|
||||
List<TreeItem> allItems = new ArrayList<>();
|
||||
|
||||
// Create group nodes
|
||||
if (groups != null && !groups.isEmpty()) {
|
||||
for (Set<IDIYComponent<?>> group : groups) {
|
||||
// Get ALL group components from raw list (for maxIndex calculation)
|
||||
List<IDIYComponent<?>> groupComponentsRaw = componentsRaw.stream()
|
||||
.filter(group::contains)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Apply search filter to get components to display
|
||||
List<IDIYComponent<?>> groupComponents = groupComponentsRaw.stream()
|
||||
.filter(component -> {
|
||||
if (searchText != null && !searchText.trim().isEmpty()) {
|
||||
return component.getName().toLowerCase().contains(searchText);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!groupComponents.isEmpty()) {
|
||||
// Sort components within the group based on sort mode
|
||||
if (sort == ExplorerPane.Sort.NAME) {
|
||||
groupComponents.sort(Comparator.comparing(x -> x.toString().toLowerCase()));
|
||||
} else if (sort == ExplorerPane.Sort.TYPE) {
|
||||
groupComponents.sort(Comparator.comparing(x -> x.getClass().getName().toLowerCase())
|
||||
.thenComparing(x -> x.toString().toLowerCase()));
|
||||
} else {
|
||||
// For Z_INDEX, sort by index in raw list (reverse order - highest first)
|
||||
groupComponents.sort((c1, c2) -> {
|
||||
int idx1 = componentsRaw.indexOf(c1);
|
||||
int idx2 = componentsRaw.indexOf(c2);
|
||||
return Integer.compare(idx2, idx1); // Reverse: higher index first
|
||||
});
|
||||
}
|
||||
|
||||
// Store both the display components and all group components (for maxIndex calculation)
|
||||
allItems.add(new TreeItem(group, groupComponents, groupComponentsRaw, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add ungrouped components
|
||||
for (IDIYComponent<?> component : this.components) {
|
||||
if (!groupedComponents.contains(component)) {
|
||||
allItems.add(new TreeItem(null, Collections.singletonList(component), Collections.singletonList(component), false));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort all items together based on sort mode
|
||||
if (sort == ExplorerPane.Sort.Z_INDEX) {
|
||||
// Sort by maximum z-order (index) - groups use max index of ALL their components (not just filtered)
|
||||
Collections.sort(allItems, (item1, item2) -> {
|
||||
int maxIndex1 = getMaxComponentIndex(item1.allComponents);
|
||||
int maxIndex2 = getMaxComponentIndex(item2.allComponents);
|
||||
// Reverse order: higher index first (since components are in reverse order)
|
||||
return Integer.compare(maxIndex2, maxIndex1);
|
||||
});
|
||||
} else if (sort == ExplorerPane.Sort.NAME) {
|
||||
Collections.sort(allItems, (item1, item2) -> {
|
||||
String name1 = item1.components.get(0).getName().toLowerCase();
|
||||
String name2 = item2.components.get(0).getName().toLowerCase();
|
||||
return name1.compareTo(name2);
|
||||
});
|
||||
} else if (sort == ExplorerPane.Sort.TYPE) {
|
||||
Collections.sort(allItems, (item1, item2) -> {
|
||||
String type1 = item1.components.get(0).getClass().getName().toLowerCase();
|
||||
String type2 = item2.components.get(0).getClass().getName().toLowerCase();
|
||||
int typeCompare = type1.compareTo(type2);
|
||||
if (typeCompare != 0) {
|
||||
return typeCompare;
|
||||
}
|
||||
String name1 = item1.components.get(0).getName().toLowerCase();
|
||||
String name2 = item2.components.get(0).getName().toLowerCase();
|
||||
return name1.compareTo(name2);
|
||||
});
|
||||
}
|
||||
|
||||
// Add sorted items to the tree
|
||||
for (TreeItem item : allItems) {
|
||||
if (item.isGroup) {
|
||||
DefaultMutableTreeNode groupNode = new DefaultMutableTreeNode(new GroupNode(item.group));
|
||||
for (IDIYComponent<?> component : item.components) {
|
||||
DefaultMutableTreeNode componentNode = new DefaultMutableTreeNode(component);
|
||||
groupNode.add(componentNode);
|
||||
}
|
||||
root.add(groupNode);
|
||||
} else {
|
||||
DefaultMutableTreeNode node = new DefaultMutableTreeNode(item.components.get(0));
|
||||
root.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify tree model listeners
|
||||
nodeStructureChanged(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class to represent a group node in the tree.
|
||||
*/
|
||||
public static class GroupNode {
|
||||
private final Set<IDIYComponent<?>> group;
|
||||
|
||||
public GroupNode(Set<IDIYComponent<?>> group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public Set<IDIYComponent<?>> getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Group (" + group.size() + " components)";
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return this.components == null ? 0 : this.components.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum index of components in the raw components list.
|
||||
* This represents the maximum z-order for the group.
|
||||
* Returns -1 if no valid index is found.
|
||||
*/
|
||||
private int getMaxComponentIndex(List<IDIYComponent<?>> groupComponents) {
|
||||
int maxIndex = -1;
|
||||
for (IDIYComponent<?> component : groupComponents) {
|
||||
int index = componentsRaw.indexOf(component);
|
||||
if (index >= 0 && index > maxIndex) {
|
||||
maxIndex = index;
|
||||
}
|
||||
}
|
||||
return maxIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to hold either a group or a single ungrouped component for sorting.
|
||||
*/
|
||||
private static class TreeItem {
|
||||
final Set<IDIYComponent<?>> group;
|
||||
final List<IDIYComponent<?>> components; // Components to display (may be filtered)
|
||||
final List<IDIYComponent<?>> allComponents; // All components for maxIndex calculation
|
||||
final boolean isGroup;
|
||||
|
||||
TreeItem(Set<IDIYComponent<?>> group, List<IDIYComponent<?>> components,
|
||||
List<IDIYComponent<?>> allComponents, boolean isGroup) {
|
||||
this.group = group;
|
||||
this.components = components;
|
||||
this.allComponents = allComponents;
|
||||
this.isGroup = isGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-213
@@ -1,213 +0,0 @@
|
||||
package org.diylc.swing.plugins.explorer;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.TransferHandler;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.lang.LangUtil;
|
||||
import org.diylc.swing.ISwingUI;
|
||||
|
||||
public class ComponentTreeTransferHandler extends TransferHandler {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String REORDERING_NOT_POSSIBLE = LangUtil.translate(
|
||||
"Component reordering is possible only when Project Explorer is sorted by z-index.");
|
||||
|
||||
private final ComponentTreeModel treeModel;
|
||||
private final ISwingUI swingUI;
|
||||
private final IPlugInPort plugInPort;
|
||||
|
||||
public ComponentTreeTransferHandler(ComponentTreeModel treeModel, ISwingUI swingUI,
|
||||
IPlugInPort plugInPort) {
|
||||
this.treeModel = treeModel;
|
||||
this.swingUI = swingUI;
|
||||
this.plugInPort = plugInPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceActions(JComponent c) {
|
||||
return MOVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transferable createTransferable(JComponent c) {
|
||||
if (!(c instanceof JTree)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JTree tree = (JTree) c;
|
||||
TreePath[] selectionPaths = tree.getSelectionPaths();
|
||||
if (selectionPaths == null || selectionPaths.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the first selected path
|
||||
TreePath firstPath = selectionPaths[0];
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) firstPath.getLastPathComponent();
|
||||
Object userObject = node.getUserObject();
|
||||
|
||||
// Only allow dragging components, not group nodes
|
||||
if (!(userObject instanceof IDIYComponent)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Store the component's group info in the transferable
|
||||
IDIYComponent<?> component = (IDIYComponent<?>) userObject;
|
||||
Set<IDIYComponent<?>> group = treeModel.getGroupForComponent(component);
|
||||
String groupId = group != null ? Integer.toString(group.hashCode()) : "null";
|
||||
|
||||
int row = tree.getRowForPath(firstPath);
|
||||
return new StringSelection(row + ":" + groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canImport(TransferHandler.TransferSupport support) {
|
||||
if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(support.getComponent() instanceof JTree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
|
||||
TreePath dropPath = dl.getPath();
|
||||
if (dropPath == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only allow reordering when sorted by Z_INDEX
|
||||
if (treeModel.getSort() != ExplorerPane.Sort.Z_INDEX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if dropping on a group node - not allowed
|
||||
DefaultMutableTreeNode dropNode = (DefaultMutableTreeNode) dropPath.getLastPathComponent();
|
||||
if (dropNode.getUserObject() instanceof ComponentTreeModel.GroupNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the source component's group from the transferable
|
||||
try {
|
||||
Transferable transferable = support.getTransferable();
|
||||
String data = (String) transferable.getTransferData(DataFlavor.stringFlavor);
|
||||
String[] parts = data.split(":");
|
||||
if (parts.length == 2) {
|
||||
String sourceGroupId = parts[1];
|
||||
|
||||
// Get the target component's group
|
||||
Object targetUserObject = dropNode.getUserObject();
|
||||
if (targetUserObject instanceof IDIYComponent) {
|
||||
IDIYComponent<?> targetComponent = (IDIYComponent<?>) targetUserObject;
|
||||
Set<IDIYComponent<?>> targetGroup = treeModel.getGroupForComponent(targetComponent);
|
||||
String targetGroupId = targetGroup != null ? Integer.toString(targetGroup.hashCode()) : "null";
|
||||
|
||||
// Only allow dragging within the same group (or both ungrouped)
|
||||
if (!sourceGroupId.equals(targetGroupId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// If we can't parse the transferable, don't allow the drop
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean importData(TransferHandler.TransferSupport support) {
|
||||
if (!canImport(support)) {
|
||||
// Show error message if not sorted by Z_INDEX
|
||||
if (treeModel.getSort() != ExplorerPane.Sort.Z_INDEX) {
|
||||
swingUI.showMessage(REORDERING_NOT_POSSIBLE, "Error", ISwingUI.ERROR_MESSAGE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(support.getComponent() instanceof JTree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
|
||||
TreePath dropPath = dl.getPath();
|
||||
|
||||
if (dropPath == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultMutableTreeNode dropNode = (DefaultMutableTreeNode) dropPath.getLastPathComponent();
|
||||
Object dropUserObject = dropNode.getUserObject();
|
||||
|
||||
// Can't drop on group nodes
|
||||
if (dropUserObject instanceof ComponentTreeModel.GroupNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(dropUserObject instanceof IDIYComponent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the parent - if it's a group node, we need to calculate index within the group
|
||||
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) dropNode.getParent();
|
||||
|
||||
if (parent != null && parent.getUserObject() instanceof ComponentTreeModel.GroupNode) {
|
||||
// Dropping within a group - calculate index within the group's children
|
||||
int childIndex = dl.getChildIndex();
|
||||
int dropTargetIndex;
|
||||
|
||||
if (childIndex != -1) {
|
||||
dropTargetIndex = childIndex;
|
||||
} else {
|
||||
// Dropping on a component within the group
|
||||
dropTargetIndex = parent.getIndex(dropNode);
|
||||
}
|
||||
|
||||
// For groups, we need to calculate z-index based on the group's components
|
||||
// Get all components in the group in z-order (they're already sorted in the model)
|
||||
ComponentTreeModel.GroupNode groupNode = (ComponentTreeModel.GroupNode) parent.getUserObject();
|
||||
List<IDIYComponent<?>> groupComponents = treeModel.getComponents().stream()
|
||||
.filter(groupNode.getGroup()::contains)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
if (dropTargetIndex < groupComponents.size()) {
|
||||
IDIYComponent<?> targetComponent = groupComponents.get(dropTargetIndex);
|
||||
// Find the component's index in the full component list
|
||||
int componentIndex = treeModel.getComponents().indexOf(targetComponent);
|
||||
if (componentIndex >= 0) {
|
||||
int targetZIndex = treeModel.getSize() - componentIndex;
|
||||
plugInPort.moveSelectionToZIndex(targetZIndex);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Dropping on an ungrouped component at root level
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
|
||||
int childIndex = dl.getChildIndex();
|
||||
int dropTargetIndex;
|
||||
|
||||
if (childIndex != -1) {
|
||||
dropTargetIndex = childIndex;
|
||||
} else {
|
||||
dropTargetIndex = root.getIndex(dropNode);
|
||||
}
|
||||
|
||||
// Calculate the target z-index (components are in reverse order for z-index)
|
||||
int targetZIndex = treeModel.getSize() - dropTargetIndex;
|
||||
plugInPort.moveSelectionToZIndex(targetZIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+206
-103
@@ -1,5 +1,6 @@
|
||||
package org.diylc.swing.plugins.explorer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
@@ -9,36 +10,47 @@ import java.awt.event.ItemListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.swing.DropMode;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.AbstractListModel;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import org.diylc.common.ComponentType;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.lang.LangUtil;
|
||||
import org.diylc.swing.ISwingUI;
|
||||
import org.diylc.swing.gui.ComponentPopupMenu;
|
||||
import org.diylc.swing.gui.DragDropList;
|
||||
import org.diylc.swing.gui.IDragDropListListener;
|
||||
import org.diylc.swing.gui.SearchTextField;
|
||||
|
||||
public class ExplorerPane extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String REORDERING_NOT_POSSIBLE = LangUtil.translate(
|
||||
"Component reordering is possible only when Project Explorer is sorted by z-index.");
|
||||
|
||||
public static Font DEFAULT_FONT = new Font("Square721 BT", Font.PLAIN, 12);
|
||||
|
||||
private ISwingUI swingUI;
|
||||
@@ -47,8 +59,8 @@ public class ExplorerPane extends JPanel {
|
||||
private JLabel titleLabel;
|
||||
private JTextField searchField;
|
||||
private JScrollPane componentScrollPane;
|
||||
private ComponentTreeModel componentTreeModel;
|
||||
private JTree componentTree;
|
||||
private ComponentListModel componentListModel;
|
||||
private JList<IDIYComponent<?>> componentList;
|
||||
private ComponentPopupMenu popupMenu;
|
||||
private JComboBox<Sort> sortBox;
|
||||
|
||||
@@ -104,7 +116,7 @@ public class ExplorerPane extends JPanel {
|
||||
public JTextField getSearchField() {
|
||||
if (searchField == null) {
|
||||
searchField = new SearchTextField(true, 'X', text -> {
|
||||
getComponentTreeModel().setSearchText(text);
|
||||
getComponentListModel().setSearchText(text);
|
||||
});
|
||||
}
|
||||
return searchField;
|
||||
@@ -117,7 +129,7 @@ public class ExplorerPane extends JPanel {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
getComponentTreeModel().setSort((Sort) e.getItem());
|
||||
getComponentListModel().setSort((Sort) e.getItem());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -126,37 +138,38 @@ public class ExplorerPane extends JPanel {
|
||||
|
||||
public JScrollPane getComponentScrollPane() {
|
||||
if (componentScrollPane == null) {
|
||||
componentScrollPane = new JScrollPane(getComponentTree(),
|
||||
componentScrollPane = new JScrollPane(getComponentList(),
|
||||
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
}
|
||||
return componentScrollPane;
|
||||
}
|
||||
|
||||
public JTree getComponentTree() {
|
||||
if (componentTree == null) {
|
||||
componentTree = new JTree(getComponentTreeModel());
|
||||
componentTree.setRootVisible(false);
|
||||
componentTree.setShowsRootHandles(true);
|
||||
componentTree.setFont(DEFAULT_FONT);
|
||||
componentTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
|
||||
componentTree.setCellRenderer(new ComponentTreeCellRenderer(plugInPort.getComponentTypes()));
|
||||
// Set row height after renderer is set, using a fixed value
|
||||
componentTree.setRowHeight(24);
|
||||
|
||||
// Enable drag and drop
|
||||
componentTree.setDragEnabled(true);
|
||||
componentTree.setDropMode(DropMode.INSERT);
|
||||
componentTree.setTransferHandler(new ComponentTreeTransferHandler(getComponentTreeModel(),
|
||||
swingUI, plugInPort));
|
||||
|
||||
componentTree.addTreeSelectionListener(new TreeSelectionListener() {
|
||||
public JList<IDIYComponent<?>> getComponentList() {
|
||||
if (componentList == null) {
|
||||
componentList = new DragDropList<IDIYComponent<?>>(new IDragDropListListener() {
|
||||
|
||||
@Override
|
||||
public void valueChanged(TreeSelectionEvent e) {
|
||||
if (e.getNewLeadSelectionPath() == null)
|
||||
public boolean dropComplete(int selectedIndex, int dropTargetIndex) {
|
||||
if (getComponentListModel().getSort() != Sort.Z_INDEX) {
|
||||
swingUI.showMessage(REORDERING_NOT_POSSIBLE, "Error", ISwingUI.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
plugInPort.moveSelectionToZIndex(getComponentListModel().getSize() - dropTargetIndex);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
componentList.setModel(getComponentListModel());
|
||||
componentList.setFont(DEFAULT_FONT);
|
||||
componentList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
componentList.setCellRenderer(new ComponentListCellRenderer(plugInPort.getComponentTypes()));
|
||||
componentList.addListSelectionListener(new ListSelectionListener() {
|
||||
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (e.getValueIsAdjusting())
|
||||
return;
|
||||
|
||||
List<IDIYComponent<?>> selectedValuesList = getSelectedComponents();
|
||||
List<IDIYComponent<?>> selectedValuesList = componentList.getSelectedValuesList();
|
||||
if (selectedValuesList.size() > 0 && !new HashSet<IDIYComponent<?>>(selectedValuesList)
|
||||
.equals(new HashSet<IDIYComponent<?>>(plugInPort.getSelectedComponents()))) {
|
||||
plugInPort.setSelection(selectedValuesList, true);
|
||||
@@ -164,7 +177,7 @@ public class ExplorerPane extends JPanel {
|
||||
}
|
||||
});
|
||||
|
||||
componentTree.addMouseListener(new MouseAdapter() {
|
||||
componentList.addMouseListener(new MouseAdapter() {
|
||||
|
||||
private MouseEvent pressedEvent;
|
||||
|
||||
@@ -177,7 +190,7 @@ public class ExplorerPane extends JPanel {
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
componentTree.requestFocus();
|
||||
componentList.requestFocus();
|
||||
pressedEvent = e;
|
||||
}
|
||||
|
||||
@@ -192,7 +205,7 @@ public class ExplorerPane extends JPanel {
|
||||
if (plugInPort.getNewComponentTypeSlot() == null && (e.isPopupTrigger()
|
||||
|| (pressedEvent != null && pressedEvent.isPopupTrigger()))) {
|
||||
|
||||
final List<IDIYComponent<?>> selectedValues = getSelectedComponents();
|
||||
final List<IDIYComponent<?>> selectedValues = componentList.getSelectedValuesList();
|
||||
getComponentPopupMenu().prepareAndShowAt(selectedValues, selectedValues, e.getX(),
|
||||
e.getY());
|
||||
}
|
||||
@@ -201,103 +214,193 @@ public class ExplorerPane extends JPanel {
|
||||
}
|
||||
});
|
||||
}
|
||||
return componentTree;
|
||||
}
|
||||
|
||||
private List<IDIYComponent<?>> getSelectedComponents() {
|
||||
List<IDIYComponent<?>> selectedComponents = new ArrayList<>();
|
||||
TreePath[] selectionPaths = componentTree.getSelectionPaths();
|
||||
if (selectionPaths != null) {
|
||||
for (TreePath path : selectionPaths) {
|
||||
Object lastPathComponent = path.getLastPathComponent();
|
||||
if (lastPathComponent instanceof DefaultMutableTreeNode) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) lastPathComponent;
|
||||
Object userObject = node.getUserObject();
|
||||
if (userObject instanceof IDIYComponent) {
|
||||
selectedComponents.add((IDIYComponent<?>) userObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return selectedComponents;
|
||||
return componentList;
|
||||
}
|
||||
|
||||
public ComponentTreeModel getComponentTreeModel() {
|
||||
if (componentTreeModel == null) {
|
||||
componentTreeModel = new ComponentTreeModel();
|
||||
public ComponentListModel getComponentListModel() {
|
||||
if (componentListModel == null) {
|
||||
componentListModel = new ComponentListModel();
|
||||
}
|
||||
return componentTreeModel;
|
||||
return componentListModel;
|
||||
}
|
||||
|
||||
public ComponentPopupMenu getComponentPopupMenu() {
|
||||
if (popupMenu == null) {
|
||||
popupMenu = new ComponentPopupMenu(plugInPort, getComponentTree(), false);
|
||||
popupMenu = new ComponentPopupMenu(plugInPort, getComponentList(), false);
|
||||
}
|
||||
return popupMenu;
|
||||
}
|
||||
|
||||
public void setComponents(List<IDIYComponent<?>> components,
|
||||
Collection<IDIYComponent<?>> selectedComponents) {
|
||||
// Get groups from the current project
|
||||
Set<Set<IDIYComponent<?>>> groups = null;
|
||||
if (plugInPort.getCurrentProject() != null) {
|
||||
groups = plugInPort.getCurrentProject().getGroups();
|
||||
}
|
||||
this.getComponentTreeModel().setComponents(components, groups);
|
||||
this.getComponentListModel().setComponents(components);
|
||||
setSelection(new HashSet<IDIYComponent<?>>(selectedComponents));
|
||||
|
||||
// Expand all group nodes after model is updated
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JTree tree = getComponentTree();
|
||||
for (int i = 0; i < tree.getRowCount(); i++) {
|
||||
TreePath path = tree.getPathForRow(i);
|
||||
if (path != null) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
|
||||
if (node.getUserObject() instanceof ComponentTreeModel.GroupNode) {
|
||||
tree.expandPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setSelection(Set<IDIYComponent<?>> selectedComponents) {
|
||||
List<IDIYComponent<?>> currentSelected = getSelectedComponents();
|
||||
if (new HashSet<IDIYComponent<?>>(currentSelected)
|
||||
List<IDIYComponent<?>> selectedValuesList = componentList.getSelectedValuesList();
|
||||
if (new HashSet<IDIYComponent<?>>(selectedValuesList)
|
||||
.equals(new HashSet<IDIYComponent<?>>(plugInPort.getSelectedComponents())))
|
||||
return;
|
||||
|
||||
final JTree tree = this.getComponentTree();
|
||||
final JList<IDIYComponent<?>> list = this.getComponentList();
|
||||
|
||||
if (selectedComponents.size() == 0) {
|
||||
tree.clearSelection();
|
||||
list.clearSelection();
|
||||
} else {
|
||||
List<TreePath> pathsToSelect = new ArrayList<>();
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) getComponentTreeModel().getRoot();
|
||||
|
||||
for (int i = 0; i < root.getChildCount(); i++) {
|
||||
DefaultMutableTreeNode child = (DefaultMutableTreeNode) root.getChildAt(i);
|
||||
Object userObject = child.getUserObject();
|
||||
if (userObject instanceof IDIYComponent && selectedComponents.contains(userObject)) {
|
||||
pathsToSelect.add(new TreePath(child.getPath()));
|
||||
}
|
||||
|
||||
int[] indices = new int[selectedComponents.size()];
|
||||
int n = 0;
|
||||
List<IDIYComponent<?>> components = this.getComponentListModel().getComponents();
|
||||
for (int i = 0; i < components.size(); i++) {
|
||||
indices[n] = -1; // this should not happen
|
||||
if (selectedComponents.contains(components.get(i)))
|
||||
indices[n++] = i;
|
||||
if (n == indices.length)
|
||||
break;
|
||||
}
|
||||
|
||||
tree.getSelectionModel().setSelectionPaths(pathsToSelect.toArray(new TreePath[0]));
|
||||
|
||||
// If none of the selected items is visible, scroll to the first one
|
||||
if (!pathsToSelect.isEmpty()) {
|
||||
TreePath firstPath = pathsToSelect.get(0);
|
||||
if (tree.getRowForPath(firstPath) == -1) {
|
||||
tree.scrollPathToVisible(firstPath);
|
||||
}
|
||||
}
|
||||
list.getSelectionModel().setValueIsAdjusting(true);
|
||||
list.setSelectedIndices(indices);
|
||||
list.getSelectionModel().setValueIsAdjusting(false);
|
||||
int firstVisibleIndex = list.getFirstVisibleIndex();
|
||||
int lastVisibleIndex = list.getFirstVisibleIndex();
|
||||
// if none of the selected items is visible, jump to the first one
|
||||
if (!Arrays.stream(indices)
|
||||
.anyMatch(index -> firstVisibleIndex <= index && index <= lastVisibleIndex))
|
||||
list.ensureIndexIsVisible(indices[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private class ComponentListModel extends AbstractListModel<IDIYComponent<?>> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
static enum Sort {
|
||||
private List<IDIYComponent<?>> componentsRaw;
|
||||
private List<IDIYComponent<?>> components;
|
||||
|
||||
private String searchText;
|
||||
private Sort sort = Sort.Z_INDEX;
|
||||
|
||||
public void setComponents(List<IDIYComponent<?>> components) {
|
||||
this.componentsRaw = components;
|
||||
int oldSize = getSize();
|
||||
|
||||
refresh(oldSize);
|
||||
}
|
||||
|
||||
public List<IDIYComponent<?>> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
public Sort getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Sort sort) {
|
||||
this.sort = sort;
|
||||
|
||||
refresh(getSize());
|
||||
}
|
||||
|
||||
public void setSearchText(String searchText) {
|
||||
this.searchText = searchText.toLowerCase();
|
||||
|
||||
refresh(getSize());
|
||||
}
|
||||
|
||||
private void refresh(int oldSize) {
|
||||
if (sort == Sort.NAME) {
|
||||
this.components =
|
||||
componentsRaw.stream().sorted(Comparator.comparing(x -> x.toString().toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
} else if (sort == Sort.TYPE) {
|
||||
this.components = componentsRaw.stream()
|
||||
.sorted(Comparator.comparing(x -> x.getClass().getName().toLowerCase())
|
||||
.thenComparing(x -> x.toString().toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
List<IDIYComponent<?>> componentsClone = new ArrayList<IDIYComponent<?>>(componentsRaw);
|
||||
Collections.reverse(componentsClone);
|
||||
this.components = componentsClone;
|
||||
}
|
||||
|
||||
if (searchText != null && !searchText.trim().isEmpty()) {
|
||||
this.components = this.components.stream().filter(component -> {
|
||||
return component.getName().toLowerCase().contains(searchText);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int newSize = this.components.size();
|
||||
|
||||
fireContentsChanged(this, 0, Math.min(newSize, oldSize));
|
||||
if (oldSize > newSize)
|
||||
fireIntervalRemoved(this, newSize, oldSize);
|
||||
else if (newSize > oldSize)
|
||||
fireIntervalAdded(this, oldSize, newSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.components == null ? 0 : this.components.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDIYComponent<?> getElementAt(int index) {
|
||||
return this.components.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ComponentListCellRenderer implements ListCellRenderer<IDIYComponent<?>> {
|
||||
|
||||
// private static Color[] layerColors = new Color[] { Color.gray, Color.decode("#ff6363"),
|
||||
// Color.decode("#293462"), Color.decode("#138588"),
|
||||
// Color.decode("#c0ca03"), Color.decode("#990000") };
|
||||
|
||||
protected DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer();
|
||||
|
||||
private Map<String, ComponentType> componentTypes;
|
||||
|
||||
public ComponentListCellRenderer(Map<String, List<ComponentType>> componentTypeMap) {
|
||||
this.componentTypes = componentTypeMap.values().stream().flatMap(x -> x.stream())
|
||||
.collect(Collectors.toMap(x -> x.getInstanceClass().getCanonicalName(), x -> x));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<? extends IDIYComponent<?>> list,
|
||||
IDIYComponent<?> value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
|
||||
ComponentType componentType = componentTypes.get(value.getClass().getCanonicalName());
|
||||
|
||||
JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index,
|
||||
isSelected, cellHasFocus);
|
||||
|
||||
int layerId = (int) Math.round(componentType.getZOrder());
|
||||
|
||||
String valueForDisplay = value.getValueForDisplay();
|
||||
if (isSelected) {
|
||||
if (valueForDisplay == null || valueForDisplay.trim().isEmpty()) {
|
||||
renderer.setText(String.format("[L%s] %s", layerId, value.getName()));
|
||||
} else {
|
||||
renderer
|
||||
.setText(String.format("[L%s] %s (%s)", layerId, value.getName(), valueForDisplay));
|
||||
}
|
||||
} else {
|
||||
if (valueForDisplay == null || valueForDisplay.trim().isEmpty()) {
|
||||
renderer.setText(String.format("<html><font color='#c0c0c0'>[L%s]</font> %s</html>",
|
||||
layerId, value.getName()));
|
||||
} else {
|
||||
renderer.setText(String.format(
|
||||
"<html><font color='#c0c0c0'>[L%s]</font> %s (<font color='#666666'>%s</font>)</html>",
|
||||
layerId, value.getName(), valueForDisplay));
|
||||
}
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
|
||||
private static enum Sort {
|
||||
Z_INDEX("Sort by Z-index"), NAME("Sort by Name"), TYPE("Sort by Type");
|
||||
|
||||
private String label;
|
||||
|
||||
Reference in New Issue
Block a user