mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-08 04:44:55 +10:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92a742d167 | |||
| 6fa0e7d56a | |||
| 12726338ef | |||
| f3a8e3cfb1 | |||
| 1ac8ba8a24 | |||
| 7b5bb3a152 | |||
| ce7753744a | |||
| 3adaa16537 | |||
| aa4f4c9c66 | |||
| ef01d52905 | |||
| 892de712b2 | |||
| 79aa25f14c | |||
| 165b61329c | |||
| c251e5ace2 | |||
| 550e221265 | |||
| 7110897e9d | |||
| 74badd8a01 | |||
| 95eccffa4a | |||
| 35a190b02f | |||
| 92457ae0fb | |||
| 73d3dd1cd4 | |||
| cb9cd46261 | |||
| 4bd3943345 | |||
| 30bdc7fc3d | |||
| a1d31c1ae2 | |||
| 90fb3095bd | |||
| 0c468369cd | |||
| f88b0c642a | |||
| 1b8bdbe7ed | |||
| 185b5da4d5 | |||
| cd92464467 | |||
| 59e18c6be2 |
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<version>5.7.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-core</artifactId>
|
||||
|
||||
@@ -300,7 +300,7 @@ public class GerberG2DWrapper extends Graphics2D
|
||||
|
||||
@Override
|
||||
public AffineTransform getTransform() {
|
||||
return tx;
|
||||
return new AffineTransform(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,23 +101,7 @@ public class NetlistBuilder {
|
||||
|
||||
// if there are no switches, make one with 1 position so we get 1 result back
|
||||
if (switches.isEmpty())
|
||||
switches.add(new ISwitch() {
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return "Default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
switches.add(new TrivialSwitch());
|
||||
|
||||
// construct all possible combinations
|
||||
int[] positions = new int[switches.size()];
|
||||
@@ -418,7 +402,9 @@ public class NetlistBuilder {
|
||||
// handle direct connections or connections through a switch
|
||||
// when not using "includeSwitches" flag
|
||||
if (c instanceof IContinuity &&
|
||||
(!(c instanceof ISwitch) || switchPositions.isEmpty())) {
|
||||
// ugly way to check if running in "exclude switches" mode.
|
||||
(!(c instanceof ISwitch) || (switchPositions.size() == 1 &&
|
||||
switchPositions.keySet().stream().findFirst().get() instanceof TrivialSwitch))) {
|
||||
for (int i = 0; i < c.getControlPointCount() - 1; i++)
|
||||
for (int j = i + 1; j < c.getControlPointCount(); j++)
|
||||
if (((IContinuity) c).arePointsConnected(i, j))
|
||||
@@ -493,4 +479,23 @@ public class NetlistBuilder {
|
||||
return area;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class TrivialSwitch implements ISwitch {
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return "Default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
<minor>5</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-05-13 00:00:00.000 CET</releaseDate>
|
||||
<releaseDate>2025-05-26 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
@@ -313,6 +313,10 @@
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>X or Q keys sometimes do not register in secondary dialogs</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>'Auto-Edit Mode' doesn't do what it's supposed to</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>If the internet connection is present, but unstable, the application starts but nothing can be clicked until the connection restores</description>
|
||||
@@ -320,5 +324,117 @@
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
<org.diylc.appframework.update.Version>
|
||||
<versionNumber>
|
||||
<major>5</major>
|
||||
<minor>6</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-08-11 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Drill Hole' component that can be used for board mounting holes, chassis holes, faceplace holes, etc</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Simplify zoom in/out keyboard shortcuts to only plus/minus keys and zero numerical key for zoom reset to 100</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Bundle Java JRE with Linux AppImage to avoid having to install it</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Updated naming conventions Mac build packages to replace 'arm' with 'silicon' and 'x86' to 'intel' for clarity</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Allow Windows 64 bit installer to run on ARM-based Windows machine (through emulation)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>More intuitive user account creation dialog for DIYLC Cloud - showing exact requirements for password and marking optional entries</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Improved project search algorithm in DIYLC cloud</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>When a switch position is selected (i.e. set to a value other than None), DIYLC will treat the terminals that are internally connected in that switching logic as connected in analysis tools</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Increase maximum size of the backup folder from 64MB to 1GB for deeper history</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Alignment issues of some visual elements in the resulting Gerber files, e.g. DIL IC pin designators</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>'Connect to Patreon' option says that it connected account with username 'a b c' but doesn't do anything</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
<org.diylc.appframework.update.Version>
|
||||
<versionNumber>
|
||||
<major>5</major>
|
||||
<minor>7</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-12-21 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added '6P5T (5-Position 6 Pole AxLabs)' lever switch type (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added '4P3T (3-Position 4 Pole AxLabs)' lever switch type (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'DP3T (Import 5-Position 2502N)' lever switch type (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Corrected 'S1 Switch' up/down position designators (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Corrected 'Mini Toggle Switch' position names for center-OFF variants to be in the correct order (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>'Drill Hole' changes color to red when selected</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
<org.diylc.appframework.update.Version>
|
||||
<versionNumber>
|
||||
<major>5</major>
|
||||
<minor>8</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-12-21 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Improved sensitivity of 'Multimeter Probe' that required pixel-level precision to detect connectivity; improved rendering of the probe in outline mode</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Fixed underside rendering and mirroring position issues that affected only SPDT 'Mini Toggle Switch' types</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
</a>
|
||||
</java.util.Arrays_-ArrayList>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<version>5.7.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-library</artifactId>
|
||||
|
||||
@@ -41,7 +41,7 @@ import org.diylc.core.annotations.EditableProperty;
|
||||
|
||||
@ComponentDescriptor(name = "Blank Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Blank circuit board",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, transformer = SimpleComponentTransformer.class)
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, transformer = SimpleComponentTransformer.class)
|
||||
public class BlankBoard extends AbstractBoard {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -50,7 +50,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Breadboard", category = "Boards", author = "Branislav Stojkovic",
|
||||
description = "Prototyping solderless breadboard", instanceNamePrefix = "BB",
|
||||
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true, transformer = ProtoBoardTransformer.class)
|
||||
public class Breadboard extends AbstractProtoBoard {
|
||||
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
author = "Branislav Stojkovic",
|
||||
description = "David Haillant's eurorack format stripboard for prototyping and building simple modules",
|
||||
instanceNamePrefix = "BB", zOrder = IDIYComponent.BOARD,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true,
|
||||
transformer = ProtoBoardTransformer.class)
|
||||
public class EurorackStripboard extends AbstractProtoBoard {
|
||||
|
||||
@@ -42,7 +42,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Eyelet Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with eyelets",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
transformer = AbstractBoardTransformer.class, enableCache = true)
|
||||
public class EyeletBoard extends AbstractBoard {
|
||||
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ import org.diylc.utils.Constants;
|
||||
@ComponentDescriptor(name = "Marshall Style Perf Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board",
|
||||
description = "Perforated board as found on some Marshall and Trainwreck amps",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TAG,
|
||||
keywordTag = "Perf Board", transformer = AbstractBoardTransformer.class)
|
||||
public class MarshallPerfBoard extends AbstractBoard {
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Perf Board w/ Pads", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with solder pads",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TAG,
|
||||
keywordTag = "Perf Board", transformer = AbstractBoardTransformer.class, enableCache = true)
|
||||
public class PerfBoard extends AbstractBoard implements ILayeredComponent {
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "P-0+o (Proto) Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
description = "A prototyping board for modular synths, central bus with V+ GND and V-", instanceNamePrefix = "BB",
|
||||
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true, transformer = ProtoBoardTransformer.class)
|
||||
public class ProtoBoard extends AbstractProtoBoard {
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
author = "Branislav Stojkovic",
|
||||
description = "Prototyping board similar to Radio Shack 276-168B or MPJA 33304",
|
||||
instanceNamePrefix = "BB", zOrder = IDIYComponent.BOARD,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true,
|
||||
transformer = ProtoBoardTransformer.class)
|
||||
public class ProtoBoard780 extends AbstractProtoBoard {
|
||||
|
||||
@@ -42,7 +42,7 @@ import org.diylc.utils.Constants;
|
||||
@ComponentDescriptor(name = "Vero Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board",
|
||||
description = "Perforated FR4 board with copper strips connecting all holes in a row",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
transformer = VeroBoardTransformer.class, enableCache = true)
|
||||
public class VeroBoard extends AbstractVeroBoard implements ILayeredComponent {
|
||||
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
@ComponentDescriptor(name = "Copper Trace", author = "Branislav Stojkovic", category = "Connectivity",
|
||||
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "Trace",
|
||||
description = "Straight copper trace", zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
|
||||
transformer = SimpleComponentTransformer.class)
|
||||
public class CopperTrace extends AbstractLeadedComponent<Void> implements ILayeredComponent, IGerberComponentSimple {
|
||||
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Curved Trace", author = "Branislav Stojkovic", category = "Connectivity",
|
||||
instanceNamePrefix = "Trace", description = "Curved copper trace with two control points",
|
||||
zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", transformer = SimpleComponentTransformer.class,
|
||||
enableCache = true)
|
||||
public class CurvedTrace extends AbstractCurvedComponent<Void> implements ILayeredComponent, IGerberComponentSimple {
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Hookup Wire", author = "Branislav Stojkovic", category = "Connectivity",
|
||||
instanceNamePrefix = "W", description = "Flexible wire with two control points", zOrder = IDIYComponent.WIRING,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
transformer = SimpleComponentTransformer.class, enableCache = false)
|
||||
public class HookupWire extends AbstractCurvedComponent<AWG> implements IContinuity {
|
||||
|
||||
|
||||
+46
-24
@@ -28,21 +28,20 @@ import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation45;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.components.transform.SimpleComponentTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Multimeter Probe", category = "Connectivity",
|
||||
author = "Branislav Stojkovic", description = "Multimeter Probe", instanceNamePrefix = "Probe",
|
||||
@@ -59,6 +58,7 @@ public class MultimeterProbe extends AbstractTransparentComponent<Color> {
|
||||
public static Size HANDLE_DIAMETER = new Size(0.2d, SizeUnit.in);
|
||||
public static Size HANDLE_LENGTH = new Size(0.4d, SizeUnit.in);
|
||||
public static Color HANDLE_COLOR = Color.red;
|
||||
public static Size SENSITIVITY = new Size(0.05d, SizeUnit.in);
|
||||
|
||||
private Point2D.Double point = new Point2D.Double(0, 0);
|
||||
private Color handleColor = HANDLE_COLOR;
|
||||
@@ -77,27 +77,49 @@ public class MultimeterProbe extends AbstractTransparentComponent<Color> {
|
||||
Shape[] shapes = getBody();
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
|
||||
if (!outlineMode) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(PROBE_COLOR);
|
||||
g2d.fill(shapes[0]);
|
||||
g2d.setColor(handleColor);
|
||||
g2d.fill(shapes[1]);
|
||||
g2d.setComposite(oldComposite);
|
||||
// draw an invisible conductive circle around the point to increase sensitivity
|
||||
g2d.setColor(Constants.TRANSPARENT_COLOR);
|
||||
drawingObserver.startTrackingContinuityArea(true);
|
||||
double sensitivity = SENSITIVITY.convertToPixels();
|
||||
g2d.fillOval((int) (point.getX() - sensitivity), (int) (point.getY() - sensitivity),
|
||||
(int) (sensitivity * 2), (int) (sensitivity * 2));
|
||||
drawingObserver.stopTrackingContinuityArea();
|
||||
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: handleColor.darker());
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : PROBE_COLOR);
|
||||
g2d.fill(shapes[0]);
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : handleColor);
|
||||
g2d.fill(shapes[1]);
|
||||
g2d.setComposite(oldComposite);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
|
||||
Constants.DEFAULT_THEME);
|
||||
|
||||
Color handleBorderColor;
|
||||
if (outlineMode) {
|
||||
handleBorderColor = theme.getOutlineColor();
|
||||
} else {
|
||||
handleBorderColor = componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: handleColor.darker();
|
||||
}
|
||||
g2d.setColor(handleBorderColor);
|
||||
g2d.draw(shapes[1]);
|
||||
g2d.setColor(
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: PROBE_COLOR.darker());
|
||||
|
||||
Color probeBorderColor;
|
||||
if (outlineMode) {
|
||||
probeBorderColor = theme.getOutlineColor();
|
||||
} else {
|
||||
probeBorderColor = componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: PROBE_COLOR.darker();
|
||||
}
|
||||
g2d.setColor(probeBorderColor);
|
||||
g2d.draw(shapes[0]);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "PCB Terminal Block", category = "Connectivity",
|
||||
author = "Branislav Stojkovic", description = "Horizontal PCB terminal block with 5mm pitch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "TB", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "TB",
|
||||
enableCache = true, transformer = PCBTerminalBlockTransformer.class)
|
||||
public class PCBTerminalBlock extends AbstractTransparentComponent<PCBTerminalBlockCount> implements IGerberComponentSimple {
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Solder Pad", category = "Connectivity", author = "Branislav Stojkovic",
|
||||
description = "Copper solder pad, round or square", instanceNamePrefix = "Pad",
|
||||
zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
|
||||
transformer = SimpleComponentTransformer.class, enableCache = true)
|
||||
public class SolderPad extends AbstractTransparentComponent<Void>
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ import org.diylc.utils.Constants;
|
||||
@ComponentDescriptor(name = "Twisted Leads", author = "Branislav Stojkovic",
|
||||
category = "Connectivity", instanceNamePrefix = "W",
|
||||
description = "A pair of flexible leads twisted tighly together", zOrder = IDIYComponent.WIRING,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
transformer = SimpleComponentTransformer.class, enableCache = true)
|
||||
public class TwistedWire extends AbstractCurvedComponent<Void> implements IContinuity {
|
||||
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "9V Battery Snap", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "", zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "BTR",
|
||||
autoEdit = false, transformer = BatterySnapTransformer.class)
|
||||
transformer = BatterySnapTransformer.class)
|
||||
public class BatterySnap9V extends AbstractTransparentComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Cliff 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Cliff-style closed panel mount 1/4\" phono jack",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", autoEdit = false, transformer = CliffJackTransformer.class,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = CliffJackTransformer.class,
|
||||
enableCache = true)
|
||||
public class CliffJack1_4 extends AbstractMultiPartComponent<JackType> implements IGerberComponentSimple {
|
||||
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Cliff 1/8\" Jack", category = "Electro-Mechanical",
|
||||
author = "Branislav Stojkovic", description = "Cliff-style closed 1/8\" phono jack",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J",
|
||||
transformer = CliffJackTransformer.class, enableCache = true)
|
||||
public class CliffJack1_8 extends AbstractMultiPartComponent<JackType> implements IGerberComponentSimple {
|
||||
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Closed 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Enclosed panel mount 1/4\" phono jack", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "J", autoEdit = false, transformer = ClosedJackTransformer.class, enableCache = true)
|
||||
instanceNamePrefix = "J", transformer = ClosedJackTransformer.class, enableCache = true)
|
||||
public class ClosedJack1_4 extends AbstractMultiPartComponent<JackType> implements IGerberComponentSimple {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
*
|
||||
* 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.electromechanical;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import com.bancika.gerberwriter.GerberFunctions;
|
||||
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.components.transform.SimpleComponentTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.annotations.PositiveNonZeroMeasureValidator;
|
||||
import org.diylc.core.gerber.GerberLayer;
|
||||
import org.diylc.core.gerber.IGerberComponentCustom;
|
||||
import org.diylc.core.gerber.IGerberDrawingObserver;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Drill Hole", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Drill hole marker for PCB, chassis or faceplate", instanceNamePrefix = "Hole",
|
||||
zOrder = IDIYComponent.BOARD + 0.1, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
|
||||
transformer = SimpleComponentTransformer.class, enableCache = true)
|
||||
public class DrillHole extends AbstractTransparentComponent<Void>
|
||||
implements IGerberComponentCustom {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Size DIAMETER = new Size(3.0d, SizeUnit.mm);
|
||||
public static Color COLOR = Color.black;
|
||||
public static Size STROKE_WIDTH = new Size(1.0d, SizeUnit.px);
|
||||
public static Size CROSSHAIR_WIDTH = new Size(1.0d, SizeUnit.px);
|
||||
|
||||
private Size diameter = DIAMETER;
|
||||
private Color color = COLOR;
|
||||
private Point2D.Double point = new Point2D.Double(0, 0);
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
|
||||
Project project, IDrawingObserver drawingObserver,
|
||||
IGerberDrawingObserver gerberDrawingObserver) {
|
||||
|
||||
if (checkPointsClipped(g2d.getClip())) {
|
||||
return;
|
||||
}
|
||||
double holeDiameter = getDiameter().convertToPixels();
|
||||
double borderWidth = STROKE_WIDTH.convertToPixels(); // Use constant stroke width
|
||||
|
||||
// Export to Gerber drill layer
|
||||
if (gerberDrawingObserver != null) {
|
||||
GerberLayer drillLayer = GerberLayer.DrillNonPlated;
|
||||
gerberDrawingObserver.startGerberOutput(drillLayer, GerberFunctions.COMPONENT_DRILL, false);
|
||||
}
|
||||
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (getAlpha() < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * getAlpha() / MAX_ALPHA));
|
||||
}
|
||||
|
||||
// Draw white circle (hole)
|
||||
g2d.setColor(Color.white);
|
||||
g2d.fill(new Ellipse2D.Double(point.getX() - holeDiameter / 2, point.getY() - holeDiameter / 2,
|
||||
holeDiameter, holeDiameter));
|
||||
|
||||
// Draw colored border
|
||||
g2d.setColor(
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: color);
|
||||
g2d.setStroke(new BasicStroke((float) borderWidth));
|
||||
g2d.draw(new Ellipse2D.Double(point.getX() - holeDiameter / 2, point.getY() - holeDiameter / 2,
|
||||
holeDiameter, holeDiameter));
|
||||
|
||||
// Draw crosshairs
|
||||
double crosshairLength = holeDiameter * 0.4; // 40% of diameter for crosshair length (increased from 30%)
|
||||
double crosshairWidth = CROSSHAIR_WIDTH.convertToPixels(); // Use constant crosshair width
|
||||
|
||||
g2d.setStroke(new BasicStroke((float) crosshairWidth));
|
||||
|
||||
// Horizontal line
|
||||
g2d.draw(new Line2D.Double(
|
||||
point.getX() - crosshairLength / 2, point.getY(),
|
||||
point.getX() + crosshairLength / 2, point.getY()));
|
||||
|
||||
// Vertical line
|
||||
g2d.draw(new Line2D.Double(
|
||||
point.getX(), point.getY() - crosshairLength / 2,
|
||||
point.getX(), point.getY() + crosshairLength / 2));
|
||||
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
if (gerberDrawingObserver != null) {
|
||||
gerberDrawingObserver.stopGerberOutput();
|
||||
}
|
||||
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
|
||||
Project project, IDrawingObserver drawingObserver) {
|
||||
this.draw(g2d, componentState, outlineMode, project, drawingObserver, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
int diameter = getClosestOdd(width * 3 / 4); // Increased from width/2 to 3/4 of width
|
||||
int borderWidth = Math.max(1, (int) STROKE_WIDTH.convertToPixels()); // Use constant stroke width
|
||||
int crosshairLength = diameter * 2 / 5; // 40% of diameter (increased from 1/3)
|
||||
int crosshairWidth = Math.max(1, (int) CROSSHAIR_WIDTH.convertToPixels()); // Use constant crosshair width
|
||||
|
||||
// Draw white circle
|
||||
g2d.setColor(Color.white);
|
||||
g2d.fillOval((width - diameter) / 2, (height - diameter) / 2, diameter, diameter);
|
||||
|
||||
// Draw colored border
|
||||
g2d.setColor(COLOR);
|
||||
g2d.setStroke(new BasicStroke(borderWidth));
|
||||
g2d.drawOval((width - diameter) / 2, (height - diameter) / 2, diameter, diameter);
|
||||
|
||||
// Draw crosshairs
|
||||
g2d.setStroke(new BasicStroke(crosshairWidth));
|
||||
|
||||
// Horizontal line
|
||||
g2d.drawLine(
|
||||
(width - crosshairLength) / 2, height / 2,
|
||||
(width + crosshairLength) / 2, height / 2);
|
||||
|
||||
// Vertical line
|
||||
g2d.drawLine(
|
||||
width / 2, (height - crosshairLength) / 2,
|
||||
width / 2, (height + crosshairLength) / 2);
|
||||
}
|
||||
|
||||
@EditableProperty(validatorClass = PositiveNonZeroMeasureValidator.class)
|
||||
public Size getDiameter() {
|
||||
return diameter;
|
||||
}
|
||||
|
||||
public void setDiameter(Size diameter) {
|
||||
this.diameter = diameter;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Color")
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControlPointSticky(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
|
||||
return VisibilityPolicy.NEVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) {
|
||||
return point;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControlPoint(Point2D point, int index) {
|
||||
this.point.setLocation(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Void value) {}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
double diameter = getDiameter().convertToPixels();
|
||||
return new Rectangle2D.Double(point.getX() - diameter, point.getY() - diameter,
|
||||
diameter * 2, diameter * 2);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Fuse Holder (Panel)", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Panel mounted fuse holder", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "FH", autoEdit = false, enableCache = true)
|
||||
instanceNamePrefix = "FH", enableCache = true)
|
||||
public class FuseHolderPanel extends AbstractMultiPartComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "IEC Socket", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Panel mounted IEC power socket", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "IEC", autoEdit = false, enableCache = true)
|
||||
instanceNamePrefix = "IEC", enableCache = true)
|
||||
public class IECSocket extends AbstractMultiPartComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
+12
-5
@@ -59,7 +59,7 @@ import org.diylc.utils.SwitchUtils;
|
||||
|
||||
@ComponentDescriptor(name = "Mini Toggle Switch", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Panel mounted mini toggle switch", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "SW", autoEdit = false, enableCache = true, transformer = MiniToggleSwitchTransformer.class)
|
||||
instanceNamePrefix = "SW", enableCache = true, transformer = MiniToggleSwitchTransformer.class)
|
||||
public class MiniToggleSwitch extends AbstractTransparentComponent<ToggleSwitchType> implements ISwitch, ILayeredComponent, IGerberComponentSimple {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -502,10 +502,17 @@ public class MiniToggleSwitch extends AbstractTransparentComponent<ToggleSwitchT
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
if (switchType.name().endsWith("_off") && position == 2)
|
||||
return "OFF";
|
||||
return "ON" + Integer.toString(position + 1);
|
||||
public String getPositionName(int position) {
|
||||
int pos = position;
|
||||
if (switchType.name().endsWith("_off")) {
|
||||
int midPosition = (getPositionCount() - 1) / 2;
|
||||
if (position == midPosition) {
|
||||
return "OFF";
|
||||
} else if (position > midPosition) {
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
return "ON" + Integer.toString(pos + 1);
|
||||
}
|
||||
|
||||
@DynamicEditableProperty(source = ToggleSwitchPositionPropertyValueSource.class)
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Neutrik 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "1/4\" mono/stereo phono jack based on Neutrik NMJx series, PCB or Panel-mount",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", autoEdit = false, transformer = NeutrikJack1_4Transformer.class,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = NeutrikJack1_4Transformer.class,
|
||||
enableCache = true)
|
||||
public class NeutrikJack1_4 extends AbstractMultiPartComponent<JackType> {
|
||||
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ import org.diylc.utils.SwitchUtils;
|
||||
|
||||
@ComponentDescriptor(name = "Slide Switch", category = "Electro-Mechanical",
|
||||
author = "Branislav Stojkovic", description = "Panel mounted slide switch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW",
|
||||
enableCache = true, transformer = MiniToggleSwitchTransformer.class)
|
||||
public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> implements ISwitch, IGerberComponentSimple {
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Humbucker Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Double-coil humbucker guitar pickup (PAF, Mini Humbuckers, Filtertrons)",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true,
|
||||
transformer = GuitarPickupTransformer.class)
|
||||
public class HumbuckerPickup extends AbstractGuitarPickup {
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Jazz Bass Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Single coil pickup for Jazz Bass and similar guitars",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true,
|
||||
transformer = GuitarPickupTransformer.class)
|
||||
public class JazzBassPickup extends AbstractSingleOrHumbuckerPickup {
|
||||
|
||||
@@ -151,8 +151,13 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
g2d.draw(body[2]);
|
||||
|
||||
if (getShowMarkers()) {
|
||||
String[] markers =
|
||||
SwitchUtils.getSwitchingMarkers(this, getControlPointCount(), false);
|
||||
String[] markers;
|
||||
if(type == LeverSwitchType.DP3T_5pos_2502N) {
|
||||
markers = new String[]{"1", "2", "3", "4", "5", "6", "7", "8"};
|
||||
} else {
|
||||
markers = SwitchUtils.getSwitchingMarkers(this, getControlPointCount(), false);
|
||||
}
|
||||
|
||||
double theta = getAngle().getValueRad();
|
||||
g2d.setColor(MARKER_COLOR);
|
||||
|
||||
@@ -203,11 +208,13 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
|| type == LeverSwitchType.DP3T_5pos) {
|
||||
x += terminalLength;
|
||||
yOffset = 7;
|
||||
} else if (type == LeverSwitchType.DP3T_5pos_Import) {
|
||||
} else if (type == LeverSwitchType.DP3T_5pos_Import || type == LeverSwitchType.DP3T_5pos_2502N) {
|
||||
yOffset = 10;
|
||||
} else if (type == LeverSwitchType._6_WAY_OG) {
|
||||
x += terminalLength;
|
||||
yOffset = 8;
|
||||
} else if (type == LeverSwitchType._4P3T) {
|
||||
yOffset = 8;
|
||||
} else {
|
||||
yOffset = 12;
|
||||
}
|
||||
@@ -226,11 +233,21 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
- (waferLength - terminalSpacing * yOffset) / 2, waferThickness,
|
||||
waferLength));
|
||||
|
||||
if (type == LeverSwitchType._4P5T) {
|
||||
if (type == LeverSwitchType._4P5T || type == LeverSwitchType._6P5T) {
|
||||
waferArea.add(new Area(new Rectangle2D.Double(x - terminalLength / 2 - waferThickness / 2 + waferSpacing, y
|
||||
- (waferLength - terminalSpacing * 12) / 2, waferThickness,
|
||||
waferLength)));
|
||||
}
|
||||
if (type == LeverSwitchType._6P5T) {
|
||||
waferArea.add(new Area(new Rectangle2D.Double(x - terminalLength / 2 - waferThickness / 2 + waferSpacing * 2, y
|
||||
- (waferLength - terminalSpacing * 12) / 2, waferThickness,
|
||||
waferLength)));
|
||||
}
|
||||
if (type == LeverSwitchType._4P3T) {
|
||||
waferArea.add(new Area(new Rectangle2D.Double(x - terminalLength / 2 - waferThickness / 2 + waferSpacing, y
|
||||
- (waferLength - terminalSpacing * 8) / 2, waferThickness,
|
||||
waferLength)));
|
||||
}
|
||||
|
||||
double theta = getAngle().getValueRad();
|
||||
|
||||
@@ -294,6 +311,7 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
}
|
||||
break;
|
||||
case DP3T_5pos_Import:
|
||||
case DP3T_5pos_2502N:
|
||||
controlPoints = new Point2D[8];
|
||||
terminalSpacing *= 1.5;
|
||||
for (int i = 0; i < controlPoints.length; i++) {
|
||||
@@ -319,6 +337,21 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
controlPoints[i + 12] = new Point2D.Double(x + waferSpacing, y + i * terminalSpacing + (i >= 6 ? terminalSpacing : 0));
|
||||
}
|
||||
break;
|
||||
case _6P5T:
|
||||
controlPoints = new Point2D[36];
|
||||
for (int i = 0; i < controlPoints.length / 3; i++) {
|
||||
controlPoints[i] = new Point2D.Double(x, y + i * terminalSpacing + (i >= 6 ? terminalSpacing : 0));
|
||||
controlPoints[i + 12] = new Point2D.Double(x + waferSpacing, y + i * terminalSpacing + (i >= 6 ? terminalSpacing : 0));
|
||||
controlPoints[i + 24] = new Point2D.Double(x + waferSpacing * 2, y + i * terminalSpacing + (i >= 6 ? terminalSpacing : 0));
|
||||
}
|
||||
break;
|
||||
case _4P3T:
|
||||
controlPoints = new Point2D[16];
|
||||
for (int i = 0; i < controlPoints.length / 2; i++) {
|
||||
controlPoints[i] = new Point2D.Double(x, y + i * terminalSpacing + (i >= 4 ? terminalSpacing : 0));
|
||||
controlPoints[i + 8] = new Point2D.Double(x + waferSpacing, y + i * terminalSpacing + (i >= 4 ? terminalSpacing : 0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Rotate if needed
|
||||
@@ -447,7 +480,16 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
|| (index2 == 11 && index2 - index1 == 5 - position);
|
||||
case _4P5T:
|
||||
return ((index1 == 0 || index1 == 12) && index2 - index1 == position + 1)
|
||||
|| ((index2 == 11 || index2 == 23) && index2 - index1 == 5 - position);
|
||||
|| ((index2 == 11 || index2 == 23) && index2 - index1 == 5 - position);
|
||||
case _6P5T:
|
||||
return ((index1 == 0 || index1 == 12 || index1 == 24) && index2 - index1 == position + 1)
|
||||
|| ((index2 == 11 || index2 == 23 || index2 == 35) && index2 - index1 == 5 - position);
|
||||
case _4P3T:
|
||||
return ((index1 == 0 || index1 == 8) && index2 - index1 == position + 1)
|
||||
|| ((index2 == 7 || index2 == 15) && index2 - index1 == 3 - position);
|
||||
case DP3T_5pos_2502N:
|
||||
positionConnections = DP3T_5pos_2502N_CONNECTIONS.get(position);
|
||||
break;
|
||||
}
|
||||
|
||||
if (positionConnections != null) {
|
||||
@@ -526,6 +568,32 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
) // position 6
|
||||
);
|
||||
|
||||
private static final List<List<int[]>> DP3T_5pos_2502N_CONNECTIONS = Arrays.asList(
|
||||
Arrays.asList(
|
||||
new int[] { 0, 3 }
|
||||
), // position 1
|
||||
Arrays.asList(
|
||||
new int[] { 0, 3 },
|
||||
new int[] { 1, 3 },
|
||||
new int[] { 4, 7 },
|
||||
new int[] { 5, 7 }
|
||||
), // position 2
|
||||
Arrays.asList(
|
||||
new int[] { 0, 3 },
|
||||
new int[] { 2, 3 },
|
||||
new int[] { 6, 7 }
|
||||
), // position 3
|
||||
Arrays.asList(
|
||||
new int[] { 1, 3 },
|
||||
new int[] { 2, 3 },
|
||||
new int[] { 5, 7 }
|
||||
), // position 4
|
||||
Arrays.asList(
|
||||
new int[] { 2, 3 },
|
||||
new int[] { 6, 7 }
|
||||
) // position 5
|
||||
);
|
||||
|
||||
@DynamicEditableProperty(source = LeverSwitchPositionPropertyValueSource.class)
|
||||
@EditableProperty(name = "Selected Position")
|
||||
@Override
|
||||
@@ -559,7 +627,10 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
_4P5T("4P5T (Super/Mega)", 5),
|
||||
DP4T("DP4T (4-Position Tele)", 4),
|
||||
_6_WAY_OG("DP4T (6-Position Oak Grigsby)", 6),
|
||||
DP5T("DP5T", 5);
|
||||
DP5T("DP5T", 5),
|
||||
_6P5T("6P5T (5-Position 6 Pole AxLabs)", 5),
|
||||
_4P3T("4P3T (3-Position 4 Pole AxLabs)", 3),
|
||||
DP3T_5pos_2502N("DP3T (Import 5-Position 2502N)", 5);
|
||||
|
||||
private final int positionCount;
|
||||
private String title;
|
||||
|
||||
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "P-90 Single Coil Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Single coil P-90 guitar pickup, both \"dog ear\" and \"soap bar\"",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true)
|
||||
public class P90Pickup extends AbstractSingleOrHumbuckerPickup {
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "P- Bass Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Split-coil pickup for P-Bass and similar guitars",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true,
|
||||
transformer = GuitarPickupTransformer.class)
|
||||
public class PBassPickup extends AbstractSingleOrHumbuckerPickup {
|
||||
|
||||
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "S1 Switch", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Fender S1 4 pole pushbutton switch", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "SW", autoEdit = false, enableCache = true)
|
||||
instanceNamePrefix = "SW", enableCache = true)
|
||||
public class S1Switch extends AbstractTransparentComponent<Void> implements ISwitch {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -208,9 +208,9 @@ public class S1Switch extends AbstractTransparentComponent<Void> implements ISwi
|
||||
|
||||
double dy = 0;
|
||||
if (i % 3 == 1) {
|
||||
dy = -1;
|
||||
} else if (i % 3 == 0) {
|
||||
dy = 1;
|
||||
} else if (i % 3 == 0) {
|
||||
dy = -1;
|
||||
}
|
||||
|
||||
Stroke stroke = g2d.getStroke();
|
||||
@@ -280,9 +280,9 @@ public class S1Switch extends AbstractTransparentComponent<Void> implements ISwi
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
if (position == 0) {
|
||||
return "Up";
|
||||
return "Down";
|
||||
}
|
||||
return "Down";
|
||||
return "Up";
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Markers")
|
||||
|
||||
@@ -54,7 +54,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Single Coil Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Single coil guitar pickup, both Strat and Tele style",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true,
|
||||
transformer = GuitarPickupTransformer.class)
|
||||
public class SingleCoilPickup extends AbstractSingleOrHumbuckerPickup {
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ import java.awt.geom.*;
|
||||
|
||||
@ComponentDescriptor(name = "Stingray Bass Pickup", category = "Guitar", author = "JD",
|
||||
description = "Humbucker Bass Pickup for Musicman Stingray and similar guitars",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram",
|
||||
enableCache = true, transformer = GuitarPickupTransformer.class)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.diylc.core.annotations.ComponentDescriptor;
|
||||
|
||||
@ComponentDescriptor(name = "Ellipse", author = "Branislav Stojkovic", category = "Shapes", instanceNamePrefix = "ELL",
|
||||
description = "Elliptical area", zOrder = IDIYComponent.COMPONENT, flexibleZOrder = true,
|
||||
bomPolicy = BomPolicy.SHOW_ALL_NAMES, autoEdit = false, transformer = SimpleComponentTransformer.class)
|
||||
bomPolicy = BomPolicy.SHOW_ALL_NAMES, transformer = SimpleComponentTransformer.class)
|
||||
public class Ellipse extends AbstractShapeWithDimensions {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.diylc.core.annotations.EditableProperty;
|
||||
|
||||
@ComponentDescriptor(name = "Polygon", author = "Branislav Stojkovic", category = "Shapes",
|
||||
instanceNamePrefix = "POLY", description = "Polygonal area", zOrder = IDIYComponent.COMPONENT,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.SHOW_ALL_NAMES, autoEdit = false,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.SHOW_ALL_NAMES,
|
||||
transformer = SimpleComponentTransformer.class)
|
||||
public class Polygon extends AbstractShape {
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Rectangle", author = "Branislav Stojkovic", category = "Shapes",
|
||||
instanceNamePrefix = "RECT", description = "Ractangular area, with or withouth rounded edges",
|
||||
zOrder = IDIYComponent.COMPONENT, flexibleZOrder = true, bomPolicy = BomPolicy.SHOW_ALL_NAMES, autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, flexibleZOrder = true, bomPolicy = BomPolicy.SHOW_ALL_NAMES,
|
||||
transformer = SimpleComponentTransformer.class)
|
||||
public class Rectangle extends AbstractShapeWithDimensions {
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Tape Measure", author = "Branislav Stojkovic", category = "Misc",
|
||||
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "TM", description = "Measures distance between the two points",
|
||||
zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
transformer = SimpleComponentTransformer.class)
|
||||
public class TapeMeasure extends AbstractLeadedComponent<String> {
|
||||
|
||||
|
||||
+5
-2
@@ -27,6 +27,7 @@ import java.awt.geom.Point2D;
|
||||
import org.diylc.common.IComponentTransformer;
|
||||
import org.diylc.common.OrientationHV;
|
||||
import org.diylc.components.electromechanical.MiniToggleSwitch;
|
||||
import org.diylc.components.electromechanical.ToggleSwitchType;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class MiniToggleSwitchTransformer implements IComponentTransformer {
|
||||
@@ -107,7 +108,8 @@ public class MiniToggleSwitchTransformer implements IComponentTransformer {
|
||||
MiniToggleSwitch sw = (MiniToggleSwitch) component;
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
double dx = 2 * (center.getX() - sw.getControlPoint(1).getX());
|
||||
double dx = 2 * (center.getX() -
|
||||
(sw.getControlPoint(0).getX() + sw.getControlPoint(sw.getControlPointCount() - 1).getX()) / 2);
|
||||
double dy = 0;
|
||||
|
||||
for (int i = 0; i < sw.getControlPointCount(); i++) {
|
||||
@@ -116,7 +118,8 @@ public class MiniToggleSwitchTransformer implements IComponentTransformer {
|
||||
}
|
||||
} else {
|
||||
double dx = 0;
|
||||
double dy = 2 * (center.getY() - sw.getControlPoint(1).getY());
|
||||
double dy = 2 * (center.getY() -
|
||||
(sw.getControlPoint(0).getY() + sw.getControlPoint(sw.getControlPointCount() - 1).getY()) / 2);
|
||||
|
||||
for (int i = 0; i < sw.getControlPointCount(); i++) {
|
||||
Point2D p = sw.getControlPoint(i);
|
||||
|
||||
@@ -28,6 +28,34 @@ public class LeverSwitchTests extends AbstractSwitchTests {
|
||||
leverSwitch.setValue(LeverSwitchType._4P5T);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeverSwitch6P5T() {
|
||||
LeverSwitch leverSwitch = new LeverSwitch();
|
||||
|
||||
String[] validCombinations = new String[] {
|
||||
"0,0,1", "0,6,11", "0,12,13", "0,18,23", "0,24,25", "0,30,35",
|
||||
"1,0,2", "1,7,11", "1,12,14", "1,19,23", "1,24,26", "1,31,35",
|
||||
"2,0,3", "2,8,11", "2,12,15", "2,20,23", "2,24,27", "2,32,35",
|
||||
"3,0,4", "3,9,11", "3,12,16", "3,21,23", "3,24,28", "3,33,35",
|
||||
"4,0,5", "4,10,11", "4,12,17", "4,22,23", "4,24,29", "4,34,35"
|
||||
};
|
||||
leverSwitch.setValue(LeverSwitchType._6P5T);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeverSwitch4P3T() {
|
||||
LeverSwitch leverSwitch = new LeverSwitch();
|
||||
|
||||
String[] validCombinations = new String[] {
|
||||
"0,0,1", "0,4,7", "0,8,9", "0,12,15",
|
||||
"1,0,2", "1,5,7", "1,8,10", "1,13,15",
|
||||
"2,0,3", "2,6,7", "2,8,11", "2,14,15",
|
||||
};
|
||||
leverSwitch.setValue(LeverSwitchType._4P3T);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeverSwitchImport() {
|
||||
@@ -50,4 +78,20 @@ public class LeverSwitchTests extends AbstractSwitchTests {
|
||||
leverSwitch.setValue(LeverSwitchType._6_WAY_OG);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeverSwitchImport2502N() {
|
||||
LeverSwitch leverSwitch = new LeverSwitch();
|
||||
|
||||
String[] validCombinations = new String[] {
|
||||
"0,0,3",
|
||||
"1,0,3", "1,1,3", "1,4,7", "1,5,7",
|
||||
"2,0,3", "2,2,3", "2,6,7",
|
||||
"3,1,3", "3,2,3", "3,5,7",
|
||||
"4,2,3", "4,6,7",
|
||||
};
|
||||
|
||||
leverSwitch.setValue(LeverSwitchType.DP3T_5pos_2502N);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,58 @@
|
||||
#!/bin/bash
|
||||
#set -x # Enable debug mode
|
||||
set -x # Enable debug mode
|
||||
|
||||
# Get the AppImage directory
|
||||
if [ -n "$APPDIR" ]; then
|
||||
HERE="$APPDIR/usr/bin"
|
||||
JRE_PATH="$APPDIR/usr/lib/jre/bin/java"
|
||||
JRE_DIR="$APPDIR/usr/lib/jre"
|
||||
else
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
JRE_PATH="$(dirname "$HERE")/lib/jre/bin/java"
|
||||
JRE_DIR="$(dirname "$HERE")/lib/jre"
|
||||
fi
|
||||
|
||||
echo "diylc.sh: Running from directory: $HERE"
|
||||
echo "diylc.sh: Looking for JRE at: $JRE_PATH"
|
||||
|
||||
# Use system Java
|
||||
JAVA="$(which java)"
|
||||
if [ -z "$JAVA" ] || [ ! -x "$JAVA" ]; then
|
||||
echo "diylc.sh: ERROR - System Java not found or not executable!"
|
||||
echo "diylc.sh: Please install Java 17 or later"
|
||||
# List directory contents for debugging
|
||||
echo "diylc.sh: Contents of $(dirname "$JRE_PATH"):"
|
||||
ls -la "$(dirname "$JRE_PATH")"
|
||||
|
||||
# Use bundled JRE
|
||||
if [ ! -f "$JRE_PATH" ]; then
|
||||
echo "diylc.sh: ERROR - Bundled JRE not found at: $JRE_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "diylc.sh: Using system Java: $JAVA"
|
||||
# Check if we're running from a mounted AppImage
|
||||
if [[ "$HERE" == /tmp/.mount_* ]]; then
|
||||
echo "diylc.sh: Running from mounted AppImage"
|
||||
# Create a temporary directory for the JRE
|
||||
TEMP_DIR="/tmp/diylc-jre-$$"
|
||||
mkdir -p "$TEMP_DIR"
|
||||
|
||||
# Copy the entire JRE directory to the temporary location
|
||||
echo "diylc.sh: Copying JRE to temporary location: $TEMP_DIR"
|
||||
cp -r "$JRE_DIR"/* "$TEMP_DIR/"
|
||||
chmod -R +x "$TEMP_DIR/bin"
|
||||
JRE_PATH="$TEMP_DIR/bin/java"
|
||||
|
||||
# Clean up the temporary directory on exit
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
fi
|
||||
|
||||
# Verify permissions
|
||||
echo "diylc.sh: Verifying JRE permissions..."
|
||||
ls -la "$JRE_PATH"
|
||||
|
||||
if [ ! -x "$JRE_PATH" ]; then
|
||||
echo "diylc.sh: ERROR - JRE is not executable at: $JRE_PATH"
|
||||
echo "diylc.sh: Current permissions: $(ls -l "$JRE_PATH")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "diylc.sh: Using JRE: $JRE_PATH"
|
||||
|
||||
cd "$HERE"
|
||||
echo "diylc.sh: Current working directory: $(pwd)"
|
||||
@@ -26,15 +60,17 @@ echo "diylc.sh: Current working directory: $(pwd)"
|
||||
# Debug: Print command before execution
|
||||
echo "diylc.sh: Executing DIYLCStarter with Java"
|
||||
|
||||
exec "$JAVA" \
|
||||
-Xms1024m \
|
||||
-Xmx4096m \
|
||||
-cp "$APPDIR/usr/lib/diylc.jar" \
|
||||
--add-opens java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens java.base/java.lang=ALL-UNNAMED \
|
||||
--add-opens java.base/java.text=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt.font=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt.geom=ALL-UNNAMED \
|
||||
-Dorg.diylc.scriptRun=true \
|
||||
org.diylc.DIYLCStarter "$@"
|
||||
# Execute Java directly with the command array
|
||||
exec "$JRE_PATH" \
|
||||
-Xms1024m \
|
||||
-Xmx4096m \
|
||||
-cp "$(dirname "$HERE")/lib/diylc.jar" \
|
||||
--add-opens java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens java.base/java.lang=ALL-UNNAMED \
|
||||
--add-opens java.base/java.text=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt.font=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt.geom=ALL-UNNAMED \
|
||||
-Dorg.diylc.scriptRun=true \
|
||||
org.diylc.DIYLCStarter "$@"
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ ChangesAssociations=yes
|
||||
AlwaysShowDirOnReadyPage=yes
|
||||
; Only allow the installer to run on x64-compatible systems,
|
||||
; and enable 64-bit install mode.
|
||||
ArchitecturesAllowed={#arch}
|
||||
ArchitecturesInstallIn64BitMode={#arch}
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
|
||||
[Files]
|
||||
Source: "diylc-{#arch}.exe"; DestDir: "{app}"; DestName: "diylc.exe"
|
||||
Source: "diylc-x64.exe"; DestDir: "{app}"; DestName: "diylc.exe"
|
||||
Source: "..\..\src\main\resources\icons\diylc_file.ico"; DestDir: "{app}"
|
||||
Source: "..\..\target\diylc.jar"; DestDir: "{app}"
|
||||
Source: "..\..\target\jre_win\*"; DestDir: "{app}\jre17"; Flags: ignoreversion recursesubdirs
|
||||
|
||||
@@ -182,6 +182,7 @@ DP3T (Standard 5-Position Strat)
|
||||
DP4T (4-Position Tele)
|
||||
DP4T (6-Position Oak Grigsby)
|
||||
DP5T
|
||||
6P5T
|
||||
DPDT
|
||||
DPDT (On/Off/On)
|
||||
DPDT (On/On)
|
||||
|
||||
+64
-12
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<version>5.7.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-swing</artifactId>
|
||||
@@ -317,12 +317,49 @@
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Create AppDir structure and copy files -->
|
||||
<!-- Download and extract JRE -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>download-extract-jre</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<!-- Clean old jre folder -->
|
||||
<delete dir="${jre.dest.linux}" />
|
||||
|
||||
<!-- Create folder to hold extracted JRE -->
|
||||
<mkdir dir="${jre.dest.linux}" />
|
||||
|
||||
<!-- Download JRE for Linux -->
|
||||
<mkdir dir="${project.build.directory}/downloads" />
|
||||
<get src="${jdk.url.linux}"
|
||||
dest="${project.build.directory}/downloads/${jdk.archive.linux}"
|
||||
usetimestamp="true" />
|
||||
|
||||
<!-- Extract JRE -->
|
||||
<untar src="${project.build.directory}/downloads/${jdk.archive.linux}"
|
||||
dest="${jre.dest.linux}"
|
||||
compression="gzip" />
|
||||
|
||||
<!-- Move files up one level -->
|
||||
<move todir="${jre.dest.linux}">
|
||||
<fileset dir="${jre.dest.linux}/jdk-${jdk.version}-jre" />
|
||||
</move>
|
||||
|
||||
<!-- Remove empty top-level folder -->
|
||||
<delete dir="${jre.dest.linux}/jdk-${jdk.version}-jre" />
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<!-- Create AppDir structure and copy files -->
|
||||
<execution>
|
||||
<id>create-appdir</id>
|
||||
<phase>package</phase>
|
||||
@@ -339,7 +376,22 @@
|
||||
|
||||
<!-- Copy app files -->
|
||||
<copy file="${project.build.directory}/diylc.jar" todir="${appdir.dir}/usr/lib" />
|
||||
|
||||
|
||||
<!-- Copy JRE -->
|
||||
<copy todir="${appdir.dir}/usr/lib/jre">
|
||||
<fileset dir="${jre.dest.linux}" />
|
||||
</copy>
|
||||
|
||||
<!-- Make JRE files executable -->
|
||||
<chmod perm="755">
|
||||
<fileset dir="${appdir.dir}/usr/lib/jre/bin" includes="**/*" />
|
||||
</chmod>
|
||||
|
||||
<!-- Ensure JRE binaries are executable in the source directory too -->
|
||||
<chmod perm="755">
|
||||
<fileset dir="${jre.dest.linux}/bin" includes="**/*" />
|
||||
</chmod>
|
||||
|
||||
<!-- Copy AppImage specific files -->
|
||||
<copy file="${project.basedir}/deploy/linux/appimage/diylc.sh" tofile="${appdir.dir}/usr/bin/diylc.sh" />
|
||||
<copy file="${project.basedir}/deploy/linux/appimage/AppRun" tofile="${appdir.dir}/AppRun" />
|
||||
@@ -581,7 +633,7 @@
|
||||
<argument>${project.basedir}:/workspace</argument>
|
||||
<argument>amake/innosetup</argument>
|
||||
<argument>/Ddiylcver=${project.version}</argument>
|
||||
<argument>/Darch=x64</argument>
|
||||
<!--<argument>/Darch=win64 arm64</argument>-->
|
||||
<argument>/workspace/deploy/win/install.iss</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
@@ -839,11 +891,11 @@
|
||||
|
||||
<echo message="Creating unsigned zip for distribution..."/>
|
||||
<!-- Create unsigned zip for distribution -->
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-x86-unsigned.zip">
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-intel-unsigned.zip">
|
||||
<zipfileset filemode="755" dir="${project.build.directory}/DIYLC.app" prefix="DIYLC.app" />
|
||||
<zipfileset dir="${project.basedir}/deploy/mac" includes="README.txt" />
|
||||
</zip>
|
||||
<echo message="Unsigned zip created at: ${project.build.directory}/diylc-${project.version}-osx-x86-unsigned.zip"/>
|
||||
<echo message="Unsigned zip created at: ${project.build.directory}/diylc-${project.version}-osx-intel-unsigned.zip"/>
|
||||
|
||||
<!-- First remove existing signatures -->
|
||||
<echo message="Removing any existing signatures..."/>
|
||||
@@ -984,10 +1036,10 @@
|
||||
|
||||
<echo message="Creating final signed and notarized zip package..."/>
|
||||
<!-- Create final signed and notarized zip package -->
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-x86.zip">
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-intel.zip">
|
||||
<zipfileset filemode="755" dir="${project.build.directory}/DIYLC.app" prefix="DIYLC.app" />
|
||||
</zip>
|
||||
<echo message="Final package created at: ${project.build.directory}/diylc-${project.version}-osx-x86.zip"/>
|
||||
<echo message="Final package created at: ${project.build.directory}/diylc-${project.version}-osx-intel.zip"/>
|
||||
<echo message="Mac build process completed successfully!"/>
|
||||
</target>
|
||||
</configuration>
|
||||
@@ -1153,11 +1205,11 @@
|
||||
|
||||
<echo message="Creating unsigned zip for distribution..."/>
|
||||
<!-- Create unsigned zip for distribution -->
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-arm-unsigned.zip">
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-silicon-unsigned.zip">
|
||||
<zipfileset filemode="755" dir="${project.build.directory}/DIYLC.app" prefix="DIYLC.app" />
|
||||
<zipfileset dir="${project.basedir}/deploy/mac" includes="README.txt" />
|
||||
</zip>
|
||||
<echo message="Unsigned zip created at: ${project.build.directory}/diylc-${project.version}-osx-arm-unsigned.zip"/>
|
||||
<echo message="Unsigned zip created at: ${project.build.directory}/diylc-${project.version}-osx-silicon-unsigned.zip"/>
|
||||
|
||||
<!-- First remove existing signatures -->
|
||||
<echo message="Removing any existing signatures..."/>
|
||||
@@ -1298,10 +1350,10 @@
|
||||
|
||||
<echo message="Creating final signed and notarized zip package..."/>
|
||||
<!-- Create final signed and notarized zip package -->
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-arm.zip">
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-silicon.zip">
|
||||
<zipfileset filemode="755" dir="${project.build.directory}/DIYLC.app" prefix="DIYLC.app" />
|
||||
</zip>
|
||||
<echo message="Final package created at: ${project.build.directory}/diylc-${project.version}-osx-arm.zip"/>
|
||||
<echo message="Final package created at: ${project.build.directory}/diylc-${project.version}-osx-silicon.zip"/>
|
||||
<echo message="Mac build process completed successfully!"/>
|
||||
</target>
|
||||
</configuration>
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.diylc.appframework.miscutils.PropertyInjector;
|
||||
import org.diylc.appframework.miscutils.Utils;
|
||||
import org.diylc.swingframework.export.DrawingExporter;
|
||||
import org.diylc.swingframework.fonts.FontOptimizer;
|
||||
import org.diylc.utils.DPIScalingUtils;
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.core.IView;
|
||||
@@ -137,6 +138,12 @@ public class DIYLCStarter {
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
|
||||
// Scale all UIManager font defaults for high DPI displays
|
||||
// This applies globally to all Swing components, fixing font scaling
|
||||
// issues on Windows with high DPI displays where HTML rendering may
|
||||
// not properly scale fonts.
|
||||
DPIScalingUtils.scaleUIManagerFonts();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Could not set Look&Feel", e);
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ public class MainFrame extends JFrame implements ISwingUI {
|
||||
|
||||
@Override
|
||||
public void injectGUIComponent(JComponent component, int position, boolean collapsible, String visibilityConfigKey) throws BadPositionException {
|
||||
LOG.info(String.format("injectGUIComponent(%s, %s, %s, %s)", component.getClass().getName(), position, collapsible, visibilityConfigKey));
|
||||
// LOG.info(String.format("injectGUIComponent(%s, %s, %s, %s)", component.getClass().getName(), position, collapsible, visibilityConfigKey));
|
||||
if (collapsible) {
|
||||
CollapsiblePanel panel = new CollapsiblePanel(position, visibilityConfigKey);
|
||||
panel.add(component);
|
||||
@@ -354,8 +354,8 @@ public class MainFrame extends JFrame implements ISwingUI {
|
||||
|
||||
@Override
|
||||
public void injectMenuAction(Action action, String menuName) {
|
||||
LOG.info(String.format("injectMenuAction(%s, %s)", action == null ? "Separator" : action.getValue(Action.NAME),
|
||||
menuName));
|
||||
// LOG.info(String.format("injectMenuAction(%s, %s)", action == null ? "Separator" : action.getValue(Action.NAME),
|
||||
// menuName));
|
||||
if (menuName != null)
|
||||
menuName = LangUtil.translate(menuName);
|
||||
|
||||
|
||||
+43
-14
@@ -55,6 +55,21 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ActionBarPlugin.class);
|
||||
|
||||
private static final SupportOption[] SUPPORT_OPTIONS = new SupportOption[] {
|
||||
new SupportOption(
|
||||
"Enjoying DIYLC? Click here to buy me a coffee :)",
|
||||
"Donate",
|
||||
IconLoader.Donate,
|
||||
HelpMenuPlugin.DONATE_URL
|
||||
),
|
||||
new SupportOption(
|
||||
"Become a Patreon and enjoy perks",
|
||||
"Join Patreon",
|
||||
IconLoader.Patreon,
|
||||
HelpMenuPlugin.PATREON_URL
|
||||
)
|
||||
};
|
||||
|
||||
private IPlugInPort plugInPort;
|
||||
private ISwingUI swingUI;
|
||||
|
||||
@@ -62,13 +77,12 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
private ActionToolbar contextActionToolbar;
|
||||
private ConfigToolbar configToolbar;
|
||||
private JLabel donateLabel;
|
||||
|
||||
private static final String DONATE_HTML = LangUtil.translate("Enjoying DIYLC? Click here to buy me a coffee :)");
|
||||
private static final String DONATE_HTML_SHORT = LangUtil.translate("Donate");
|
||||
private SupportOption selectedOption;
|
||||
private Consumer<Boolean> highlightConnectedAreasUpdateAction;
|
||||
|
||||
public ActionBarPlugin(ISwingUI swingUI) {
|
||||
this.swingUI = swingUI;
|
||||
this.selectedOption = SUPPORT_OPTIONS[(int)(Math.random() * SUPPORT_OPTIONS.length)];
|
||||
}
|
||||
|
||||
public JPanel getActionPanel() {
|
||||
@@ -110,12 +124,9 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
|
||||
public JLabel getDonateLabel() {
|
||||
if (donateLabel == null) {
|
||||
donateLabel = new JLabel(DONATE_HTML);
|
||||
// Map<TextAttribute, Integer> fontAttributes = new HashMap<TextAttribute, Integer>();
|
||||
// fontAttributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
|
||||
// donateLabel.setFont(donateLabel.getFont().deriveFont(fontAttributes));
|
||||
donateLabel = new JLabel(selectedOption.longText);
|
||||
donateLabel.setForeground(Color.blue);
|
||||
donateLabel.setIcon(IconLoader.Donate.getIcon());
|
||||
donateLabel.setIcon(selectedOption.icon.getIcon());
|
||||
donateLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
this.swingUI.getOwnerFrame().addComponentListener(new ComponentAdapter() {
|
||||
|
||||
@@ -126,11 +137,15 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
if (rootPane.getWidth() < 940) {
|
||||
if (!DONATE_HTML_SHORT.equals(donateLabel.getText()))
|
||||
donateLabel.setText(DONATE_HTML_SHORT);
|
||||
if (!selectedOption.shortText.equals(donateLabel.getText())) {
|
||||
donateLabel.setText(selectedOption.shortText);
|
||||
donateLabel.setIcon(selectedOption.icon.getIcon());
|
||||
}
|
||||
} else {
|
||||
if (!DONATE_HTML.equals(donateLabel.getText()))
|
||||
donateLabel.setText(DONATE_HTML);
|
||||
if (!selectedOption.longText.equals(donateLabel.getText())) {
|
||||
donateLabel.setText(selectedOption.longText);
|
||||
donateLabel.setIcon(selectedOption.icon.getIcon());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -140,9 +155,11 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
try {
|
||||
Utils.openURL(HelpMenuPlugin.DONATE_URL);
|
||||
Utils.openURL(selectedOption.url);
|
||||
} catch (Exception e1) {
|
||||
swingUI.showMessage("Web browser launching failed. Please visit " + HelpMenuPlugin.DONATE_URL + " to donate. Thank you!", "Error", ISwingUI.INFORMATION_MESSAGE);
|
||||
swingUI.showMessage("Web browser launching failed. Please visit " +
|
||||
selectedOption.url +
|
||||
" to support DIYLC. Thank you!", "Error", ISwingUI.INFORMATION_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -225,4 +242,16 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
OperationMode.HIGHLIGHT_CONNECTED_AREAS);
|
||||
}
|
||||
}
|
||||
|
||||
private record SupportOption(
|
||||
String longText,
|
||||
String shortText,
|
||||
IconLoader icon,
|
||||
String url
|
||||
) {
|
||||
public SupportOption {
|
||||
longText = LangUtil.translate(longText);
|
||||
shortText = LangUtil.translate(shortText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public class AutoSavePlugin implements IPlugIn {
|
||||
private static final Logger LOG = Logger.getLogger(AutoSavePlugin.class);
|
||||
|
||||
public static long BACKUP_FREQ_MS = 60 * 1000;
|
||||
public static int MAX_TOTAL_SIZE_MB = 64;
|
||||
public static int MAX_TOTAL_SIZE_MB = 1024;
|
||||
|
||||
private ExecutorService executor;
|
||||
|
||||
|
||||
@@ -146,42 +146,35 @@ public class CanvasPanel extends JComponent implements Autoscroll {
|
||||
}
|
||||
|
||||
private void initializeActions() {
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_QUOTE, 0), "repeatLast");
|
||||
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "clearSlot");
|
||||
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, KeyEvent.CTRL_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.CTRL_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, KeyEvent.META_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, KeyEvent.CTRL_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, KeyEvent.META_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.META_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "zoomIn");
|
||||
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, KeyEvent.CTRL_DOWN_MASK), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.CTRL_DOWN_MASK), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, KeyEvent.META_DOWN_MASK), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.META_DOWN_MASK), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0), "zoomIn");
|
||||
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "zoomOut");
|
||||
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, KeyEvent.CTRL_DOWN_MASK), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, KeyEvent.CTRL_DOWN_MASK), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, KeyEvent.META_DOWN_MASK), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, KeyEvent.META_DOWN_MASK), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, 0), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, 0), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), "resetZoom");
|
||||
|
||||
for (int i = 1; i <= 12; i++) {
|
||||
final int x = i;
|
||||
@@ -198,36 +191,6 @@ public class CanvasPanel extends JComponent implements Autoscroll {
|
||||
});
|
||||
}
|
||||
|
||||
// getActionMap().put("zoomIn", new AbstractAction() {
|
||||
//
|
||||
// private static final long serialVersionUID = 1L;
|
||||
//
|
||||
// @Override
|
||||
// public void actionPerformed(ActionEvent e) {
|
||||
// LOG.debug("Keyboard zoom-in triggered");
|
||||
// double oldZoom = CanvasPanel.this.plugInPort.getZoomLevel();
|
||||
// Double[] availableZoomLevels = CanvasPanel.this.plugInPort.getAvailableZoomLevels();
|
||||
// int index = Arrays.binarySearch(availableZoomLevels, oldZoom);
|
||||
// if (index < availableZoomLevels.length - 1)
|
||||
// CanvasPanel.this.plugInPort.setZoomLevel(availableZoomLevels[index + 1]);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// getActionMap().put("zoomOut", new AbstractAction() {
|
||||
//
|
||||
// private static final long serialVersionUID = 1L;
|
||||
//
|
||||
// @Override
|
||||
// public void actionPerformed(ActionEvent e) {
|
||||
// LOG.debug("Keyboard zoom-out triggered");
|
||||
// double oldZoom = CanvasPanel.this.plugInPort.getZoomLevel();
|
||||
// Double[] availableZoomLevels = CanvasPanel.this.plugInPort.getAvailableZoomLevels();
|
||||
// int index = Arrays.binarySearch(availableZoomLevels, oldZoom);
|
||||
// if (index > 0)
|
||||
// CanvasPanel.this.plugInPort.setZoomLevel(availableZoomLevels[index - 1]);
|
||||
// }
|
||||
// });
|
||||
|
||||
getActionMap().put("resetZoom", new AbstractAction() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -104,6 +104,7 @@ public class CloudPlugIn implements IPlugIn {
|
||||
getManageAccountAction().setEnabled(false);
|
||||
getChangePasswordAction().setEnabled(false);
|
||||
getLogOutAction().setEnabled(false);
|
||||
getConnectPatreonAction().setEnabled(false);
|
||||
}
|
||||
|
||||
public CloudBrowserFrame getCloudBrowser() {
|
||||
@@ -179,6 +180,7 @@ public class CloudPlugIn implements IPlugIn {
|
||||
getUploadAction().setEnabled(true);
|
||||
getManageProjectsAction().setEnabled(true);
|
||||
getChangePasswordAction().setEnabled(true);
|
||||
getConnectPatreonAction().setEnabled(true);
|
||||
}
|
||||
|
||||
public void loggedOut() {
|
||||
@@ -190,6 +192,7 @@ public class CloudPlugIn implements IPlugIn {
|
||||
getUploadAction().setEnabled(false);
|
||||
getManageProjectsAction().setEnabled(false);
|
||||
getChangePasswordAction().setEnabled(false);
|
||||
getConnectPatreonAction().setEnabled(false);
|
||||
}
|
||||
|
||||
// Getters for action classes
|
||||
|
||||
-1
@@ -60,7 +60,6 @@ public class ConnectPatreonAction extends AbstractAction {
|
||||
return;
|
||||
}
|
||||
String username = plugInPort.getCloudService().getCurrentUsername();
|
||||
username ="a b c";
|
||||
String url = "http://diy-fever.com/diylc/api/v1/ai/patreon_login.php?username=" + URLEncoder.encode(username, StandardCharsets.UTF_8);
|
||||
try {
|
||||
java.awt.Desktop.getDesktop().browse(new java.net.URI(url));
|
||||
|
||||
+58
-9
@@ -37,6 +37,7 @@ import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import org.diylc.swingframework.ButtonDialog;
|
||||
|
||||
@@ -47,6 +48,14 @@ public class UserEditDialog extends ButtonDialog {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String BULLET = " • ";
|
||||
private static final String PASSWORD_RULES_HTML = "<html>" + BULLET
|
||||
+ "Password must be at least 8 characters long<br>" + BULLET
|
||||
+ "Password must contain at least one letter<br>" + BULLET
|
||||
+ "Password must contain at least one number<br>" + BULLET
|
||||
+ "Passwords must match exactly<br>"
|
||||
+ "</html>";
|
||||
|
||||
private JPanel mainPanel;
|
||||
|
||||
private JTextField userNameField;
|
||||
@@ -65,6 +74,8 @@ public class UserEditDialog extends ButtonDialog {
|
||||
|
||||
private UserEntity existingEntity;
|
||||
|
||||
private JLabel passwordRulesLabel;
|
||||
|
||||
public UserEditDialog(JFrame owner, UserEntity existingEntity) {
|
||||
super(owner, existingEntity == null ? "New Account" : "Manage Account", new String[] {OK, CANCEL});
|
||||
|
||||
@@ -117,16 +128,33 @@ public class UserEditDialog extends ButtonDialog {
|
||||
|
||||
gbc.gridy = 2;
|
||||
mainPanel.add(new JLabel("Confirm Password:"), gbc);
|
||||
|
||||
// Add password rules hint pane
|
||||
gbc.gridy = 3;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.gridx = 0;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.insets = new Insets(4, 2, 4, 2); // Add more vertical padding
|
||||
mainPanel.add(getPasswordRulesLabel(), gbc);
|
||||
|
||||
// Reset gridwidth and gridx for remaining fields
|
||||
gbc.gridwidth = 1;
|
||||
gbc.gridx = 1;
|
||||
gbc.insets = new Insets(2, 2, 2, 2); // Reset insets
|
||||
gbc.gridy = 4;
|
||||
}
|
||||
|
||||
gbc.gridy = 3;
|
||||
gbc.gridy = existingEntity == null ? 4 : 3;
|
||||
gbc.gridx = 0;
|
||||
mainPanel.add(new JLabel("eMail:"), gbc);
|
||||
|
||||
gbc.gridy = 4;
|
||||
mainPanel.add(new JLabel("Website:"), gbc);
|
||||
gbc.gridy = existingEntity == null ? 5 : 4;
|
||||
gbc.gridx = 0;
|
||||
mainPanel.add(new JLabel("Website (optional):"), gbc);
|
||||
|
||||
gbc.gridy = 5;
|
||||
mainPanel.add(new JLabel("Short Bio:"), gbc);
|
||||
gbc.gridy = existingEntity == null ? 6 : 5;
|
||||
gbc.gridx = 0;
|
||||
mainPanel.add(new JLabel("Short Bio (optional):"), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
@@ -144,13 +172,13 @@ public class UserEditDialog extends ButtonDialog {
|
||||
mainPanel.add(getConfirmPasswordField(), gbc);
|
||||
}
|
||||
|
||||
gbc.gridy = 3;
|
||||
gbc.gridy = existingEntity == null ? 4 : 3;
|
||||
mainPanel.add(getEmailField(), gbc);
|
||||
|
||||
gbc.gridy = 4;
|
||||
gbc.gridy = existingEntity == null ? 5 : 4;
|
||||
mainPanel.add(getWebsiteField(), gbc);
|
||||
|
||||
gbc.gridy = 5;
|
||||
gbc.gridy = existingEntity == null ? 6 : 5;
|
||||
gbc.fill = GridBagConstraints.BOTH;
|
||||
mainPanel.add(getBioPane(), gbc);
|
||||
}
|
||||
@@ -161,7 +189,16 @@ public class UserEditDialog extends ButtonDialog {
|
||||
this.userName = getUserNameField().getText();
|
||||
String password = new String(getPasswordField().getPassword());
|
||||
String confirmPassword = new String(getConfirmPasswordField().getPassword());
|
||||
if (password.equals(confirmPassword)) {
|
||||
|
||||
// Password validation
|
||||
boolean hasValidPassword = true;
|
||||
if (existingEntity == null) {
|
||||
hasValidPassword = password.length() >= 8 &&
|
||||
password.matches(".*[a-zA-Z].*") && // at least one letter
|
||||
password.matches(".*\\d.*"); // at least one number
|
||||
}
|
||||
|
||||
if (password.equals(confirmPassword) && hasValidPassword) {
|
||||
this.password = password;
|
||||
} else {
|
||||
this.password = null;
|
||||
@@ -342,4 +379,16 @@ public class UserEditDialog extends ButtonDialog {
|
||||
}
|
||||
return bioArea;
|
||||
}
|
||||
|
||||
private JLabel getPasswordRulesLabel() {
|
||||
if (passwordRulesLabel == null) {
|
||||
passwordRulesLabel = new JLabel();
|
||||
passwordRulesLabel.setOpaque(true);
|
||||
passwordRulesLabel.setBackground(UIManager.getColor("ToolTip.background"));
|
||||
passwordRulesLabel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(),
|
||||
BorderFactory.createEmptyBorder(2, 2, 2, 2)));
|
||||
passwordRulesLabel.setText(PASSWORD_RULES_HTML);
|
||||
}
|
||||
return passwordRulesLabel;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ public class CloudBrowserFrame extends JFrame implements ISimpleView {
|
||||
super(TITLE);
|
||||
this.swingUI = swingUI;
|
||||
this.setIconImage(IconLoader.Cloud.getImage());
|
||||
this.setPreferredSize(new Dimension(700, 640));
|
||||
this.setPreferredSize(new Dimension(800, 640));
|
||||
this.plugInPort = plugInPort;
|
||||
this.searchSession = new SearchSession(plugInPort);
|
||||
|
||||
|
||||
@@ -63,10 +63,11 @@ public class HelpMenuPlugin implements IPlugIn {
|
||||
public static String PLUGIN_URL = "https://github.com/bancika/diy-layout-creator/blob/wiki/PluginAPI.md";
|
||||
public static String BUG_URL = "https://github.com/bancika/diy-layout-creator/issues";
|
||||
public static String DONATE_URL = "http://diy-fever.com/donate";
|
||||
public static String PATREON_URL = "http://diy-fever.com/patreon";
|
||||
|
||||
private IPlugInPort plugInPort;
|
||||
private AboutDialog aboutDialog;
|
||||
private ISwingUI swingUI;
|
||||
private final ISwingUI swingUI;
|
||||
|
||||
public HelpMenuPlugin(ISwingUI swingUI) {
|
||||
this.swingUI = swingUI;
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
*
|
||||
* 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.utils;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Toolkit;
|
||||
import java.util.Enumeration;
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
* Utility class for handling DPI scaling on high-resolution displays.
|
||||
*
|
||||
* This is particularly important on Windows where HTML rendering in Swing
|
||||
* components (like JLabel) can fail to properly scale fonts on high DPI displays.
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*/
|
||||
public class DPIScalingUtils {
|
||||
|
||||
private static Double dpiScaleFactor = null;
|
||||
private static final double SCALING_THRESHOLD = 0.85; // 85% threshold to avoid double-scaling
|
||||
private static final double OVERSCALE_THRESHOLD = 1.3; // 130% - if font is this much larger than expected, it's over-scaled
|
||||
private static final float BASE_FONT_SIZE_96DPI = 12.0f; // Standard font size at 96 DPI
|
||||
private static final float MINIMUM_FONT_SIZE = 10.0f;
|
||||
private static final float MAXIMUM_REASONABLE_FONT_SIZE = 20.0f;
|
||||
|
||||
/**
|
||||
* Gets the DPI scale factor for the current display.
|
||||
* This is particularly important on Windows with high DPI displays where
|
||||
* HTML rendering in Swing components can fail to properly scale fonts.
|
||||
*
|
||||
* @return The DPI scale factor (1.0 for normal displays, > 1.0 for high DPI)
|
||||
*/
|
||||
public static double getDpiScaleFactor() {
|
||||
if (dpiScaleFactor == null) {
|
||||
try {
|
||||
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
GraphicsDevice gd = ge.getDefaultScreenDevice();
|
||||
GraphicsConfiguration gc = gd.getDefaultConfiguration();
|
||||
|
||||
// Get the transform scale factor from GraphicsConfiguration
|
||||
// This is the most reliable way to detect DPI scaling
|
||||
double scaleX = gc.getDefaultTransform().getScaleX();
|
||||
double scaleY = gc.getDefaultTransform().getScaleY();
|
||||
double transformScale = Math.max(scaleX, scaleY);
|
||||
|
||||
// On Windows, Java's DPI awareness can be inconsistent, especially with HTML rendering
|
||||
// Check if we're on Windows and if the transform indicates scaling
|
||||
if (System.getProperty("os.name", "").toLowerCase().contains("windows")) {
|
||||
// If transform scale is > 1.0, we have DPI scaling
|
||||
// HTML rendering in Swing on Windows may not respect this, so we need to compensate
|
||||
if (transformScale > 1.0) {
|
||||
dpiScaleFactor = transformScale;
|
||||
} else {
|
||||
// Fallback: check screen resolution as a secondary indicator
|
||||
int screenResolution = Toolkit.getDefaultToolkit().getScreenResolution();
|
||||
if (screenResolution > 96) {
|
||||
// High DPI display detected via resolution
|
||||
dpiScaleFactor = screenResolution / 96.0;
|
||||
} else {
|
||||
dpiScaleFactor = 1.0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// On non-Windows systems, trust the transform scale
|
||||
dpiScaleFactor = Math.max(1.0, transformScale);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Fallback to 1.0 if there's any error
|
||||
dpiScaleFactor = 1.0;
|
||||
}
|
||||
}
|
||||
return dpiScaleFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the expected font size for a given base size at the current DPI.
|
||||
*
|
||||
* @param baseSize The base font size (typically at 96 DPI)
|
||||
* @return The expected font size at the current DPI
|
||||
*/
|
||||
private static float getExpectedFontSize(float baseSize) {
|
||||
double scaleFactor = getDpiScaleFactor();
|
||||
return (float) (baseSize * scaleFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales a font to match the DPI scaling of the display.
|
||||
* This is useful for components that use HTML rendering (like JLabel with HTML text),
|
||||
* which may not properly scale fonts on Windows with high DPI displays.
|
||||
*
|
||||
* @param font The font to scale
|
||||
* @return A scaled font if high DPI is detected and scaling is needed, otherwise the original font
|
||||
*/
|
||||
public static Font scaleFontForDPI(Font font) {
|
||||
if (font == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double scaleFactor = getDpiScaleFactor();
|
||||
|
||||
// Only apply scaling when high DPI is detected
|
||||
if (scaleFactor <= 1.0) {
|
||||
return font;
|
||||
}
|
||||
|
||||
float currentSize = font.getSize();
|
||||
|
||||
// Calculate what the base size would be (assuming current size is at 96 DPI)
|
||||
float assumedBaseSize = currentSize;
|
||||
|
||||
// Calculate what the expected size should be at this DPI
|
||||
float expectedSize = getExpectedFontSize(assumedBaseSize);
|
||||
|
||||
// Calculate the ratio of current to expected size
|
||||
double sizeRatio = currentSize / expectedSize;
|
||||
|
||||
// Only scale if the current size is significantly smaller than expected
|
||||
// This prevents double-scaling on systems that already handle it correctly
|
||||
if (sizeRatio < SCALING_THRESHOLD) {
|
||||
// Scale to expected size, but ensure minimum readability
|
||||
float scaledSize = Math.max(expectedSize, MINIMUM_FONT_SIZE);
|
||||
return font.deriveFont(scaledSize);
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downscales a font that appears to be over-scaled.
|
||||
* This calculates what the font size should be based on the actual DPI
|
||||
* and scales it down proportionally if it's significantly larger than expected.
|
||||
*
|
||||
* @param font The font to downscale
|
||||
* @return A downscaled font if needed, otherwise the original font
|
||||
*/
|
||||
private static Font downscaleFontForDPI(Font font) {
|
||||
if (font == null) {
|
||||
return font;
|
||||
}
|
||||
|
||||
float currentSize = font.getSize();
|
||||
double scaleFactor = getDpiScaleFactor();
|
||||
|
||||
// Calculate what the base size would be if we reverse the DPI scaling
|
||||
// This assumes the font was scaled from a base size at 96 DPI
|
||||
float estimatedBaseSize = (float) (currentSize / scaleFactor);
|
||||
|
||||
// Calculate what the expected size should be at this DPI
|
||||
float expectedSize = getExpectedFontSize(BASE_FONT_SIZE_96DPI);
|
||||
|
||||
// Calculate the ratio of current to expected size
|
||||
double sizeRatio = currentSize / expectedSize;
|
||||
|
||||
// If the font is significantly larger than expected (over-scaled), downscale it
|
||||
if (sizeRatio > OVERSCALE_THRESHOLD) {
|
||||
// Calculate target size: use the base size if reasonable, otherwise use expected size
|
||||
float targetSize;
|
||||
if (estimatedBaseSize >= MINIMUM_FONT_SIZE && estimatedBaseSize <= MAXIMUM_REASONABLE_FONT_SIZE) {
|
||||
// Use the calculated base size if it's reasonable
|
||||
targetSize = estimatedBaseSize;
|
||||
} else {
|
||||
// Otherwise, use the expected size for the current DPI
|
||||
targetSize = expectedSize;
|
||||
}
|
||||
|
||||
// Ensure target is within reasonable bounds
|
||||
targetSize = Math.max(MINIMUM_FONT_SIZE, Math.min(targetSize, MAXIMUM_REASONABLE_FONT_SIZE));
|
||||
|
||||
// Only apply if the reduction is significant enough to matter (>10% change)
|
||||
if (Math.abs(targetSize - currentSize) / currentSize > 0.1) {
|
||||
return font.deriveFont(targetSize);
|
||||
}
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales a font size value to match the DPI scaling of the display.
|
||||
*
|
||||
* @param fontSize The base font size to scale
|
||||
* @return The scaled font size if high DPI is detected, otherwise the original size
|
||||
*/
|
||||
public static float scaleFontSizeForDPI(float fontSize) {
|
||||
double scaleFactor = getDpiScaleFactor();
|
||||
if (scaleFactor > 1.0) {
|
||||
return (float) (fontSize * scaleFactor);
|
||||
}
|
||||
return fontSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales an integer font size value to match the DPI scaling of the display.
|
||||
*
|
||||
* @param fontSize The base font size to scale
|
||||
* @return The scaled font size if high DPI is detected, otherwise the original size
|
||||
*/
|
||||
public static int scaleFontSizeForDPI(int fontSize) {
|
||||
return Math.round(scaleFontSizeForDPI((float) fontSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the cached DPI scale factor. This is useful if the display configuration
|
||||
* changes at runtime (e.g., moving the window to a different display).
|
||||
*/
|
||||
public static void resetCache() {
|
||||
dpiScaleFactor = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current display is a high DPI display.
|
||||
*
|
||||
* @return true if the DPI scale factor is greater than 1.0
|
||||
*/
|
||||
public static boolean isHighDPIDisplay() {
|
||||
return getDpiScaleFactor() > 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales all UIManager font defaults to match the DPI scaling of the display.
|
||||
* This applies globally to all Swing components, ensuring consistent font scaling
|
||||
* across the entire application without needing to modify individual components.
|
||||
*
|
||||
* This should be called once during application initialization, after setting the
|
||||
* Look and Feel but before creating any UI components.
|
||||
*
|
||||
* On Windows: Fixes HTML rendering issues where fonts don't scale properly.
|
||||
* On Linux: May downscale fonts if they appear to be over-scaled by the system.
|
||||
* On macOS: Does nothing (macOS handles DPI scaling correctly).
|
||||
*
|
||||
* The scaling is conservative - it only scales fonts that appear to need adjustment,
|
||||
* preventing double-scaling on systems that already handle it correctly.
|
||||
*/
|
||||
public static void scaleUIManagerFonts() {
|
||||
String osName = System.getProperty("os.name", "").toLowerCase();
|
||||
boolean isWindows = osName.contains("windows");
|
||||
boolean isLinux = osName.contains("linux");
|
||||
boolean isMac = osName.contains("mac");
|
||||
|
||||
// On macOS, Java/Swing handles DPI scaling perfectly, so do nothing
|
||||
if (isMac) {
|
||||
return;
|
||||
}
|
||||
|
||||
double scaleFactor = getDpiScaleFactor();
|
||||
|
||||
try {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
Enumeration<Object> keys = defaults.keys();
|
||||
|
||||
// Get a reference font size to check if fonts are already scaled
|
||||
// Typical default font size is 12-13pt at 96 DPI
|
||||
Font defaultFont = UIManager.getFont("Label.font");
|
||||
if (defaultFont == null) {
|
||||
defaultFont = UIManager.getFont("Button.font");
|
||||
}
|
||||
|
||||
if (defaultFont == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float defaultSize = defaultFont.getSize();
|
||||
|
||||
// Calculate expected font size at current DPI (assuming base size of 12pt at 96 DPI)
|
||||
float expectedSize = getExpectedFontSize(BASE_FONT_SIZE_96DPI);
|
||||
|
||||
// Calculate the ratio of actual to expected size
|
||||
double sizeRatio = defaultSize / expectedSize;
|
||||
|
||||
// Determine if we need to scale up or down based on the ratio
|
||||
boolean needsUpScaling = false;
|
||||
boolean needsDownScaling = false;
|
||||
|
||||
if (isWindows && scaleFactor > 1.0) {
|
||||
// On Windows with high DPI, fonts may be too small due to HTML rendering issues
|
||||
// If font is significantly smaller than expected, it needs up-scaling
|
||||
if (sizeRatio < SCALING_THRESHOLD) {
|
||||
needsUpScaling = true;
|
||||
}
|
||||
} else if (isLinux) {
|
||||
// On Linux, fonts may be over-scaled by the system
|
||||
// If font is significantly larger than expected, it needs down-scaling
|
||||
if (sizeRatio > OVERSCALE_THRESHOLD) {
|
||||
needsDownScaling = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If no scaling is needed, return early
|
||||
if (!needsUpScaling && !needsDownScaling) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (keys.hasMoreElements()) {
|
||||
Object key = keys.nextElement();
|
||||
Object value = defaults.get(key);
|
||||
|
||||
// Check if this is a font property
|
||||
if (value instanceof Font) {
|
||||
Font font = (Font) value;
|
||||
Font adjustedFont = font;
|
||||
|
||||
if (needsUpScaling) {
|
||||
// Scale up for Windows (fix HTML rendering issues)
|
||||
adjustedFont = scaleFontForDPI(font);
|
||||
} else if (needsDownScaling) {
|
||||
// Scale down for Linux (fix over-scaling)
|
||||
adjustedFont = downscaleFontForDPI(font);
|
||||
}
|
||||
|
||||
// Only update if the font was actually adjusted
|
||||
if (adjustedFont != font) {
|
||||
defaults.put(key, adjustedFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Log error but don't fail - font scaling is a nice-to-have feature
|
||||
System.err.println("Warning: Could not scale UIManager fonts: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<version>5.7.0</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>DIY Layout Creator</name>
|
||||
|
||||
@@ -129,7 +129,7 @@ INSERT INTO diylc_knowledge_base (category, section, content) ('Component Types'
|
||||
"type" : "java.lang.Boolean"
|
||||
}, {
|
||||
"name" : "Type",
|
||||
"type" : "org.diylc.components.guitar.LeverSwitch$LeverSwitchType(DP3T (Standard 3-Position Strat),DP3T (Standard 5-Position Strat),DP3T (Import 5-Position Strat),4P5T (Super/Mega),DP4T (4-Position Tele),DP4T (6-Position Oak Grigsby),DP5T)"
|
||||
"type" : "org.diylc.components.guitar.LeverSwitch$LeverSwitchType(DP3T (Standard 3-Position Strat),DP3T (Standard 5-Position Strat),DP3T (Import 5-Position Strat),4P5T (Super/Mega),DP4T (4-Position Tele),DP4T (6-Position Oak Grigsby),DP5T,6P5T (5-Position 6 Pole AxLabs)),4P3T (3-Position 3 Pole AxLabs),DP3T (Import 5-Position 2502N)"
|
||||
}, {
|
||||
"name" : "Angle",
|
||||
"type" : "org.diylc.core.Angle"
|
||||
|
||||
Reference in New Issue
Block a user